补充
- 2020.7.14: extension 中的便利构造器无需填加
convenience关键字
OC 初始化器
@implementation TestView
- (instancetype)init {
NSLog(@"111 before super init");
self = [super init];
NSLog(@"111 after super init");
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
NSLog(@"111 before super initWithFrame");
self = [super initWithFrame:frame];
NSLog(@"111 after super initWithFrame");
return self;
}
@end
@implementation TestView2
- (instancetype)init {
NSLog(@"222 before super init");
self = [super init];
NSLog(@"222 after super init");
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
NSLog(@"222 before super initWithFrame");
self = [super initWithFrame:frame];
NSLog(@"222 after super initWithFrame");
return self;
}
#end
TestView2 继承自 TestView
TestView 继承自 UIView
控制器中执行 TestView2 *testView2 = [[TestView2 alloc] init];
最后的打印结果是什么?
222 before super init
111 before super init
222 before super initWithFrame
111 before super initWithFrame
111 after super initWithFrame
222 after super initWithFrame
111 after super init
222 after super init
为什么?
也就是说 oc 构造器执行的流程是什么?
先执行便利初始化器,并调用父类的便利初始化器,均执行完成后再从子类的指定初始化器执行依次往 上执行。
如果便利初始化器中不执行 [super init],指定初始化器无法被调用。
- (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
Swift 初始化器
// 均省略了 required init?(coder: NSCoder)
class A: UIView {
convenience init() {
print("111 before convenience")
self.init(frame: CGRect.zero)
print("111 after convenience")
}
override init(frame: CGRect) {
print("111 before design")
super.init(frame: CGRect.zero)
print("111 before design")
}
}
class B: A {
convenience init() {
print("222 before convenience")
self.init(frame: CGRect.zero)
print("222 after convenience")
}
override init(frame: CGRect) {
print("222 before design")
super.init(frame: frame)
print("222 after design")
}
}
控制器中执行 B()
执行结果如下
222 before convenience
222 before design
111 before design
111 before design
222 after design
222 after convenience
其实 swift 的执行结果更好理解。
只是 swift 的不能乱写,否则会报错,一定要符合原则。
类类型的初始化原则(初始化器调用的原则)
- 设计初始化器必须从他的直接父类中调用一个设计初始化器。
- 便利初始化器必须从相同的类中调用另一个初始化器。
- 便利初始化器必须最终调用一个设计初始化器。
两阶段初始化(属性初始化的原则)
- 设计始化器必须保证所有类的全部属性有值,只要全部的父类属性有值,它的内存认为完全初始化了,然后进入阶段2。
- 父类的初始化器结束,子类的设计初始化器可以执行额外的自定义,之后子类的便利初始化器可以再执行额外的自定义。
继承
- 如果没有定义设计初始化器,自动继承父类的全部设计初始化器
- 如果子类重写了父类所有的设计初始化器,自动继承父类所有的便利化初始器
换而言之,如果子类只重写了父类非全部的设计初始化器,或者子类有自己的初始化器时,子类是没有相应的便利化初始化器的。
结构体
结构体的没有便利初始化器,只有设计初始化器,而且也不存在继承,所以没有上述的复杂。但是也有要注意的点:
// 下面这段代码是不会报错的
// 如果存在指定初始化器的话,那么 a 也需要给到默认值才可以。
struct T {
var a: Int
}
Kotlin 初始化器
由于最近在做 Android 相关的开发。所以也把 kotlin 的初始化器写在了这里做一下备忘。没有遵循单一职责原则,羞愧。
整体的思路和 swift 初始化器几乎一模一样,靠着类似的机制来保证初始化的安全。
- 如果子类有主构造函数,是基类必须在主构造函数中立即初始化。
- 子类没有主构造器,则必须在每一个二级构造器中用
super关键字初始化基类,或者在代理另一个构造函数
// 如果想要子类可以继承,则必须加上 `open` 关键字
open class A(context: Context): View(context) {
// 这个 `init` 函数就相当于是主构造器的函数体
init {
println("A 主构造器执行")
}
constructor(context: Context, name: String): this(context) {
}
// 如果子类想要重写父类的方法,则也必须加上 `open` 关键字
open fun testOverride() {
}
}
open class B(context: Context): A(context) {
init {
println("B 主构造器执行")
}
}
class C: B {
// 如果有主构造器,没办法办法使用 `super(context)` 的
constructor(context: Context): super(context) {
println("C 第二构造器执行")
}
override fun testOverride() {
super.testOverride()
}
}
======================执行结果=======================
A 主构造器执行
B 主构造器执行
C 第二构造器执行
和 Swift 初始化器区别
swift 可以在 super.init() 之前打印一些信息,但其实也无法使用 self 关键字,因为 self 这个时候还没有初始化。
kotlin 无法在 super.init() 之前打印信息。
思考
如果在 class C 中增加一个 init 函数,那么这个函数中的内部会在什么时候打印呢?
参考文档: