title | excerpt | categories | tags | toc | ||
---|---|---|---|---|---|---|
重读 Swift (5.0) 第三篇 |
Inheritance、Initialization、Deinitialization |
|
|
true |
- 子类重写的方法,
someMethod()
,可以在子类重写中通过super.someMethod()
调用 - 子类重写的
property
,someProperty
,可以在子类中setter/getter
方法中通过super.someProperty
调用 - 子类重写的
subscript
,someIndex
,可以子类重写的subscript中通过super[someIndex]
调用
- 子类可以通过自定义
getter/setter
的方式对属性进行重写 - 可以将
read-only
的属性重写为read-write
的属性,反过来则不可以 let
属性不可以被override
let
存储属性和read-only
计算属性不可以被子类重写observer
- 重写
setter
和willSet
的 observer 不可以同时存在,可以在重写的setter
中实现observer
-
可以通过添加
final
关键字禁止重写,如final var, final func, final class func, final subscript
-
extension
中也可以对添加的method,property,subscript
使用final
修饰 -
也可以对整个
class
使用final
修饰
Default value is preferred
constant
属性只能在声明的类的初始化方法中修改,不能在子类初始化方法中修改
如果 structure
或者 class
所有的存储属性都有了默认值,并且没有自定义初始化方法,Swift会提供默认的初始化方法,默认初始化方法没有参数,按照属性的默认值进行初始化
-
对于值类型,一个初始化方法内部可以通过
self.init
调用另外一个初始化方法 -
自定义初始化方法后,默认的初始化方法(或者
structure
的按成员的初始化方法)方法将不可用 -
可以在
extension
中添加自定义初始化方法同时保留默认初始化方法和structure
的按成员初始化方法
- Designated Initializers
- 需要将所有的本类声明的属性做初始化,并且调用合适的父类初始化方法
- 像漏斗的尖口一样,保持尽量少,保证所有本类定义的属性都被初始化,通往父类初始化方法的入口
- 每个类至少有一个,可以从父类中继承
- Convenience Initializers
- 最终需要调用本类的
Designated Initializers
- 可以根据传入的参数做一些定制的初始化
- 可选,数量时0个或多个
- 最终需要调用本类的
-
Designated Initializer
必须调用直接父类的Designated Initializer
-
Convenience Initializer
必须调用本类的另外一个初始化方法 -
Convenience Initializer
必须最终调用到一个Designated Initializer
Designated initializers must always delegate up.
Convenience initializers must always delegate across.
Phase-1
每个本类声明的存储类型属性都必须被赋初值Phase-2
在每个对象可用之前,类可以对存储属性做进一步的改造
- 防止属性在被初始化之前被访问
- 防止属性意外地被其它初始化方法赋了不同的值?
Designated Initializer
必须保证在调用父类初始化方法之前所有本类声明的存储属性都被初始化了Designated Initializer
在给继承的属性赋值之前,必须调用了父类的初始化方法,否则赋的值会被父类的初始化方法重写Convenience Initializer
在给属性(本类定义的属性和继承的属性)赋值之前,必须调用了本类其它的初始化方法,否则赋的值会被本类的Designated Initializer
覆盖- 在
Phase-1
之前,初始化方法不能调用任何实例方法,读取实例属性的值,不能将self
作为值被引用(比如将self
当作参数初始化另一个类型)
-
Phase-1
-
designated or convenience initializer
被调用 -
分配内存,内存未被初始化
-
Designated Initializer
保证本类声明的存储属性被赋值。这些存储属性的内存被初始化 -
Designated Initializer
提交父类的初始化方法进行同样的操作 -
在继承链上持续调用父类的初始化方法直到最终父类
-
最终父类调用了初始化方法对它的存储属性进行了赋值。这时候这个新对象的内存被完全地初始化了,
phase-1
完成
-
-
Phase-2
-
Designated Initializer
这时候就可以对本类对象做一些自定义的赋值和调用,比如改变属性的值,调用实例方法等 -
Convenience Initializer
这时候可以做一些自定义的赋值和调用
-
- swift 的子类不会自动继承父类的初始化方法,以防止子类初始化时调用了父类初始化方法导致对象没有被正确的初始化(满足特定条件的时候可以自动继承)
- 子类可以重写父类的
Designated Initializer
,在方法名前加override
关键字,可重写的初始化方法包括默认初始化方法 - 当子类定义的初始化方法和父类的
Convenience Initializer
相同的时候,根据Rule 1
, 子类不可以不可以直接调用父类的Convenience Initializer
, 所以严格来说并没有重写,所以不需要添加override
关键字 - 如果子类定义的初始化方法没有在
Phase 2
对对象进行修改,并且父类有不需要参数的指定初始化方法,那么子类的初始化方法中可以省略调用父类的初始化方法,实际是对super.init()
的隐式调用
自动继承的前提是所有子类子类定义的存储属性都被赋了默认值
-
如果子类没有定义任何指定初始化方法,那么子类会自动继承父类的所有指定初始化方法
-
如果子类实现了父类的所有指定初始化方法(从规则1继承或者都重写了),那么子类会自动继承父类的所有便利构造方法
规则2中,可以将父类的指定构造方法重写为子类的便利构造方法
-
定义可失败的初始化方法的方式是在
init
后面加一个问号,如init?()
不可以同时定义相同参数的一个可失败的和一个不可失败的方法
-
当发生初始化失败时,
return nil
来直接返回 -
初始化失败的规则可以自定义,比如一些参数不符合要求,或者其它情况发生了失败
-
可失败的初始化方法返回的是一个
optional
对象类型
- 一个可失败的初始化方法可以调用本类的另一个可失败的初始化方法,一个子类的可失败的初始化方法可以调用父类可失败的初始化方法
- 一个可失败的初始化方法可以调用一个不可失败的初始化方法,可以通过这种形式对不可失败的初始化过程添加可失败的状态和表现
- 可以将父类可失败的初始化方法在子类重写为不可失败的初始化方法,反之不可以
- 父类可失败的初始化方法重写为不可失败的初始化方法时, 只能用强制解包的方式调用父类可失败的初始化方法,也可以调用父类不可失败的指定初始化方法
- init? 可以调用 init!,反之也可以
- init? 可以重写为 init!,反之也可以
- init 可以调用 init!,反之也可以
- 被required标记的初始化方法,子类必须重写
- 子类重写时,前面也要写
required
关键字,不写override
关键字 - 如果子类满足继承初始化方法的条件,可以不用显式重写
deinitializer
在对象deallocate
之前被立马调用- 父类的
deinit
在子类的deinit
结束之后自动调用 - 对象在
deinit
调用结束之前还没有被释放,所以deinitializer
可以访问所有的属性,比如通过保存的文件名关闭打开的文件
Q1: 为什么本类的初始化要放在 super 调用之前?反过来会有什么问题?
因为子类有可能重写了父类的方法,而父类的初始化方法中又可能调用了重写的方法,如果子类的方法重写中读取了子类新增的属性,父类在子类初始化之前调用就会出错。
所以需要满足上述的 Safety Check 1