0%

深入理解 [super init]

开始

  创建一个 Test 测试类, 重写初始化方法:

1
2
3
4
5
6
7
- (instancetype)init {
self = [super init];
if (self) {
// do something...
}
return self;
}

疑问:

  • 为何要执行 [super init] ?
  • [super init] 的结果为何要 self 接收?

探索:

  • [super init]

  这一句看起来很有迷惑性, 一个 super 关键字, 好像初始化的是父类, 但在码代码的时候可以注意一个细节: 在写 self 时, 代码提示是有返回值类型的, 而写 [super init] 时, 并没有返回值类型, 但在别处写 super 时, 自动提示的返回值类型是父类.

  这表示在 [super init] 这句话中, super 并不是指父类, 可能只是一个没有什么卵用的关键字, 但在苹果的官方文档中, 这么写是推荐的写法, 那么这句话就很关键了.

  通过 clang -rewrite-objc Test.m 命令, 重新编译成 cpp 文件, 可以看到这一行代码究竟做了什么:

1
2
3
4
5
6
7
static instancetype _I_Test_init(Test * self, SEL _cmd) {
self = ((Test *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Test"))}, sel_registerName("init"));
if (self) {

}
return self;
}

  经过简化后, [super init] 就变为

1
objc_msgSendSuper({self, class_getSuperclass(objc_getClass("Test"))}, sel_registerName("init"));

  而其中 objc_msgSendSuper() 这个方法, API 文档是这样描述的:

1
OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )  OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0); 

Sends a message with a simple return value to the superclass of an instance of a class.
将具有返回值的消息发送到一个实例的超类.

  第一个参数是 objc_suoer 类型的结构体, 第二个或更多是 SEL 方法选择器, 而在 runtime.m 文档中, objc_super 的结构为:

1
2
3
4
struct objc_super {
__unsafe_unretained id receiver;
__unsafe_unretained Class super_class;
};

  receiver 是实例对象, super_class 是用来接收消息的类, 为实例对象的父类.

  在当前的代码里, receiverself , super_classNSObject .

  再回过头来, 注意看 [super init] 的 c++ 源码, 返回值为 Test *, 所以在这里的 init 只是向上初始化父类而已.

  那么这就明了了, [super init] 只是为了将父类, 父类的父类, 父类的父类的父类等等等等, 从 NSObject 开始的所有类都初始化了一遍, 只是为了确保父类的方法, 属性都能正确使用而已.

  • self = [super init]

  既然明白了 [super init] 做了什么, 那么返回结果再赋值给 self 就基本没有疑问了: 如果在 [super init] 这一步因为一些不明的原因导致初始化失败, 那么返回值应该是为 nil 的, 这时候让 self 接收一下, 之后用 if 判断, 则可以避免一些 BUG.

总结

  • 为何要执行 [super init] ?
    • 为了将当前实例的父类树进行初始化, 以保证继承父类树的所有属性与方法.
  • [super init] 的结果为何要 self 接收?
    • 为了确保初始化不会因为失败而 crash.