开始
创建一个 Test
测试类, 重写初始化方法:
- (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 文件, 可以看到这一行代码究竟做了什么:
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]
就变为
objc_msgSendSuper({self, class_getSuperclass(objc_getClass("Test"))}, sel_registerName("init"));
而其中 objc_msgSendSuper()
这个方法, API 文档是这样描述的:
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
的结构为:
struct objc_super {
__unsafe_unretained id receiver;
__unsafe_unretained Class super_class;
};
receiver
是实例对象, super_class
是用来接收消息的类, 为实例对象的父类.
在当前的代码里, receiver
为 self
, super_class
为 NSObject
.
再回过头来, 注意看 [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.
网友评论