我正在从一个在线课程中学习 iOS 开发,每次我制作一个自定义视图(自定义表格视图单元格、集合视图单元格等) ,老师总是实现这个初始化器:
required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
为什么我总是要调用这个函数? 它是做什么的? 我可以把属性放到 init 中吗?
实现该初始化程序的需求是由两个因素造成的:
Liskov代换原则.如果 S 是 T 的一个子类(例如 MyViewController是 ViewController的一个子类) ,那么 S 对象(MyViewController的实例)必须能够在期望 T 对象(ViewController的实例)的地方被替换。
MyViewController
ViewController
如果在子类中显式定义了任何初始值设定项,则不会在 Swift 中继承初始值设定项。如果显式提供了一个初始值设定项,那么必须显式提供所有其他初始值设定项(它可以调用 super.init(...))。请参阅 这个问题了解基本原理。这是 Java 语言,但仍然适用。
super.init(...)
到了第1点,原来的 ViewController可以做的所有事情,MyViewController子类应该都可以做了。其中之一就是能够从给定的 NSCoder初始化。到了第2点,您的 MyViewController子类不会自动继承这个能力。因此,必须手动提供满足此要求的初始化程序。在这种情况下,您只需要委托给超类,让它做它通常会做的事情。
NSCoder
我将从相反的方向开始回答这个问题: 如果您想将视图的状态保存到磁盘上,该怎么办?这就是 序列化。相反的是 反序列化-从磁盘恢复对象的状态。
NSCoding协议定义了序列化和反序列化对象的两种方法:
NSCoding
encodeWithCoder(_ aCoder: NSCoder) { // Serialize your object here } init(coder aDecoder: NSCoder) { // Deserialize your object here }
那么,为什么在您的定制类中需要它呢?答案是 Interface Builder。当你将一个对象拖到故事板上并配置它时,Interface Builder 会将该对象的状态序列化到磁盘上,然后当故事板出现在屏幕上时反序列化它。你得告诉 Interface Builder 怎么做。至少,如果您没有向子类添加任何新属性,您可以简单地要求超类为您进行打包和解包,因此 super.init(coder: aDecoder)调用。如果子类更复杂,则需要为子类添加自己的序列化和反序列化代码。
super.init(coder: aDecoder)
这与 VisualStudio 的方法相反,VisualStudio 的方法是将代码写入隐藏文件,以便在运行时生成对象。