Table of Content

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 也没什么卵用,并不代表一个实例,只是从这个类开始找方法。虽然他和被指定的实例是一个东西。