为啥出口不能在 initWithCoder 中初始化?

Posted

技术标签:

【中文标题】为啥出口不能在 initWithCoder 中初始化?【英文标题】:Why outlet cannot be init within initWithCoder?为什么出口不能在 initWithCoder 中初始化? 【发布时间】:2018-09-06 20:07:45 【问题描述】:

我们都知道,一旦我们在 View 或其 ViewController 中放置一个出口,它就会被标记为未包装,我们都知道 Swift 想要在初始化阶段初始化所有属性,这就是我们给出的句子谁第一次问我们为什么一个出口总是带有感叹号。

今天我试图理解为什么来自 XIB 的对象不能在 initWithCoder: 方法中初始化。

据我所知,XIB 文件仅包含有关使用 XML 文件结构在 XIB 内部绘制的对象的所有信息。 所以我们在 XIB 文件中看到的内容将被归档并存储到一个文件中。

当我们调用UINibloadNibNamed:owner:options:类方法时,它会解压之前创建的对象,查找所有属性,设置它们并向对象发送消息awakeFromNib...

但是由于那个感叹号说“在初始化阶段我无法初始化你”,我上面所说的应该是不正确的。但是为什么呢? 谁能告诉我为什么 Nib 不能被初始化并且应该被标记为可选?

这里有一些来自 Apple 的文档对我没有帮助 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html

【问题讨论】:

【参考方案1】:

您的@IBOutlet 属性由您的UIViewController 子类引入。

说“Swift 想要在初始化阶段初始化所有属性”有点过于简单了。

Swift 初始化规则规定子类引入的所有属性必须在调用超类初始化器之前进行初始化,并且 Swift 编译器必须能够“看到”这个初始化;必须有明确的分配。这是“安全检查 1”:-

安全检查 1

指定的初始化程序必须确保其类引入的所有属性在委托给超类初始化程序之前都已初始化。

在您使用 XIB 或情节提要场景的几乎所有情况下,您都不会覆盖 init(coder:),因此编译器可以确定您没有为这些属性显式赋值

如果您确实覆盖了初始化程序并分配了值(或者即使您在声明属性时只是分配了默认值),那么您可以将它们设为普通属性,而不是隐式展开的可选项,但这有点毫无意义,因为您会加载 XIB 时几乎立即覆盖这些值。

隐式展开的可选选项不会说“在初始化阶段我无法初始化你”;它更像是“我知道它看起来没有被初始化,但在运行时它会被初始化。相信我”(严格来说它只是声明一个可选的,允许为nil,所以编译器不会'不要抱怨它没有被初始化,而是在引用它时隐式强制解包该属性 - 因此名称为“隐式解包可选”)。

这适用于@IBOutlets,因为加载过程使用Key Value Coding 在运行时分配值。

这就是为什么如果您删除 @IBOutlet 但忘记更新 XIB/Storyboard 您会收到运行时异常,指出您的类“不符合 xxx 的键/值”。

以这种方式使用隐式解包选项通常被认为是可以接受的,因为您会很快发现在测试期间是否有连接问题(因为您的应用程序会因“意外为零”而崩溃)并且可以节省很多有条件的展开。

【讨论】:

首先感谢您的回答。是的,我知道隐式可选并不意味着“在初始化阶段我无法初始化你”,但它只是为了总结以及当您尝试为不存在的设置值时的 KVC 错误处理钥匙。顺便说一句,我认为您的回答的第一部分抓住了我的问题。在大多数情况下,我们不会覆盖initWithCoder:,因此我们引入一些出口的子类不会为这些属性分配值。 没错。在初始化过程中在某处分配一个值是不够的; Swift 需要查看子类中分配的值。 我现在的想法是为什么苹果从来没有自动化这个过程。我的意思是:为什么Apple不自动添加覆盖初始化和一行解码指令以避免使用可选?所以我们可以在初始化过程中轻松设置所有对象 首先是因为 Swift 已经移植到基于 Objective-C 的 UIKit 之上。 Obj-C 中不存在初始化安全检查,但您可以有效地获得相同的错误(即不符合 KV 的崩溃),或者可以说是更糟糕的错误;不分配给 Obj-C 插座只会导致什么都没有发生(因为您可以向nil 发送消息),而不是您在 Swift 中遇到的显式崩溃。其次,因为它可以说会更糟;每个 VC 中都会有很多样板代码行,每次添加出口时都需要添加一个新的分配行

以上是关于为啥出口不能在 initWithCoder 中初始化?的主要内容,如果未能解决你的问题,请参考以下文章

你在哪里设置子视图的图层属性?为啥不在 initWithCoder 中

为啥我不能将来自文件所有者的 navigationItem 出口添加到导航项?

来自笔尖的 NSWindowController 子类初始化不使用 -initWithCoder:?

覆盖 initWithCoder 时的无限循环

如何在 UIView 的子类中同时覆盖 initWithFrame: 和 initWithCoder:?

我应该在这里使用awakeFromNib还是initWithCoder?