0

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

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

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

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

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

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

4

1 回答 1

3

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

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

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

安全检查 1

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

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

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

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

这适用于@IBOutlets,因为加载过程使用键值编码在运行时分配值。

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

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

于 2018-09-06T20:49:45.460 回答