Objective-C 的 Runtime 运行时(二)
5 月份的时候写过一篇Objective-C 的 runtime 运行时(一)。
这次主要是说消息机制的。
上次我们说
runtime.age = 19;
转换成 objc_msgSend 就是
objc_msgSend((id)runtime, sel_registerName("setAge:"), (NSInteger)19);
大概是这么回事 id objc_msgSend(id self, SEL op, ...)
。
self 是第一个参数,后面一个 SEL,self 回头再讲,今天先研究下 SEL,他的消息到底发给谁。
#import "Cat.h"
@implementation Cat
- (instancetype)init {
if (self = [super init]) {
NSLog(@"%@", [self className]);
NSLog(@"%@", [super className]);
}
return self;
}
@end
那两句 NSLog,转成运行时代码就是
objc_msgSend((id)self, sel_registerName("class"))));
objc_msgSendSuper((__rw_objc_super){self, class_getSuperclass(objc_getClass("Cat"))}, sel_registerName("class"))));
看不懂是吧,我也看不懂,散了吧散了吧。
开个玩笑

objc_msgSendSuper
大概是这么回事 id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
objc_super
就是图上的 __rw_objc_super
就是一个结构体 (__rw_objc_super){self, class_getSuperclass(objc_getClass("Cat"))}
返回一个 __rw_objc_super
类型的结构体,然后剩下的就和 objc_msgSend
一样了。
其实这块我还真不懂,莫非说是 objc_msgSendSuper
是给 super
类发消息?这里咱们留一个疑问,objc_msgSendSuper 和 objc_msgSend 到底给谁发消息。来做个测试。
网上流行一道题,问
- (instancetype)init {
if (self = [super init]) {
NSLog(@"%@", [self className]);
NSLog(@"%@", [super className]);
}
return self;
}
打印结果是什么,正确答案是都是当前类的名字
2015-12-13 23:41:30.721 runtime[7825:857680] Cat
2015-12-13 23:41:30.723 runtime[7825:857680] Cat
如果在当前类复写这个方法
#import "Cat.h"
@implementation Cat
- (instancetype)init {
if (self = [super init]) {
NSLog(@"%@", [self className]);
NSLog(@"%@", [super className]);
}
return self;
}
- (NSString *)className {
return @"Cat";
}
@end
正确答案是
2015-12-13 23:45:11.731 runtime[7913:866259] Cat
2015-12-13 23:45:11.732 runtime[7913:866259] Cat
What happened?
第一个 Cat,还能理解,肯定是当前实例调用自己的方法,然后就打印 Cat 咯,第二个什么鬼,莫非是 objc_msgSendSuper
真的会给当前实例发消息?
非也
如果方法这么改
- (NSString *)className {
return @"Cattttt";
}
聪明的同学已经猜到了答案
2015-12-13 23:47:32.255 runtime[7933:870835] Cattttt
2015-12-13 23:47:32.256 runtime[7933:870835] Cat
聪明的同学可能已经大概知道 objc_msgSend
是个什么鬼了,发送消息,就是发送给该发送给的对象,如果自己有这个方法,那就调用自己的,如果自己没有,就往上级查找去。此处为什么还是 Cat 呢,咱们留个疑问
如果我们在 Animal 类中也写一个方法,经过上次的教训,我已经不敢写真实的类名了
- (NSString *)className {
return @"Animalllll";
}
结果是
2015-12-13 23:50:05.171 runtime[7952:875359] Cattttt
2015-12-13 23:50:05.172 runtime[7952:875359] Animalllll
来解决刚才的疑问,既然 [super className]
是优先调用自己类的 className
方法,那为毛他总是显示结果是 Cat
,super 肯定是 Animal 啊。
其实这个问题我就不清楚了,我说真的,不过根据猜测,来看 __rw_objc_super
这个结构体
struct __rw_objc_super {
struct objc_object *object;
struct objc_object *superClass;
__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {}
};
他第一个成员 object
,我猜这货八成就是上面 id objc_msgSend(id self, SEL op, ...)
里面的 self
,不过仅限猜测,superClass
可能只是一个标志符之类的东西,只是说,从这个类开始查找方法,并不实际起作用。其实这个猜测的前半部分是错的,咱们后面讲
为了验证这个说法,我把 runtime 的源码搞来看了一看,原来 objc_super
结构体是长这个样子的
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained Class class;
#else
__unsafe_unretained Class super_class;
#endif
/* super_class is the first class to search */
};
#endif
receiver
是被指定的一个实例,super_class
是被指定的一个 接受消息
的实例。
那消息是什么,消息不就是 objc_msgSend((id)runtime, sel_registerName("setAge:"), (NSInteger)19);
里面的 sel_registerName("setAge:"), (NSInteger)19)
。
那说白了,就是从传入的这个类开始寻找方法,实际被指定的实例,还是当前类,也就是说 objc_msgSendSuper((__rw_objc_super){self, class_getSuperclass(objc_getClass("Cat"))}, sel_registerName("class"))));
只是给 __rw_objc_super
发消息,从这里开始找 class
方法,实际作为 receiver
的,是当前的类 self
,也就是 Cat
。
刚才那个猜测,现在清楚了,id objc_msgSend(id self, SEL op, ...)
里面的 self
也没什么卵用,并不代表一个实例,只是从这个类开始找方法。虽然他和被指定的实例是一个东西。