虽说苹果公司早就公开了Objective-C的Runtime源码,然而我却并没有深入了解过Runtime,实在汗颜,于是趁着不太忙的时候自己结合着网上的一些相关文章研究一下,然后记录自己对Runtime的理解。鉴于本人能力有限,自己的理解如有误,还望大家能够指正。(说明一下:文中的代码,左右滑动即可查看全部代码内容)
runtime源码地址,我这里引用的代码版本是objc4-709。众所周知,Objective-C是面向对象编程的语言,那么在Objective-C的世界观里面,万物皆对象,包括我们熟知的Class。
对象(id)和类(Class)以及元类(MetaClass)的关系
我们首先来看一看Runtime中Class的定义:
从上面的代码可以看出Class的本质就是一个指向objc_class结构体的指针。
我们再看一下Object的定义:
从上面的代码可以看出id的本质就是一个指向objc_object结构体的指针。
结合Class和id的定义,我们发现在objc_class结构体里面,有指向其所属类的isa指针,这就意味着,其实我们所讲到的Class也是一个对象,既然类也是对象,那么类对象所属的类是什么呢?于是就出现了元类MetaClass,而元类所属的类就是根元类NSObject MetaClass。为了能自圆其说,规定根元类是最终的元类,它所属的类就是它本身,从而能够形成闭环。
实例(id)、类(Class)、元类(MetaClass)关系图:
关系图佐证代码:
void test (){
// 继承关系 Man : Person : NSObject
Person *p = [[Person alloc] init];
Man *m = [[Man alloc] init];
/* isa */
// 对象p的所属类就是Person类对象(Person Class)
Class cls = object_getClass(p);
BOOL clsIsMeta = class_isMetaClass(cls);
NSLog(@"cls == %@ clsIsMeta = %@ ",cls, @(clsIsMeta));
// Person类对象的所属类就是Person元类(Person MetaClass)
Class meta = object_getClass(cls);
BOOL metaIsMeta = class_isMetaClass(meta);
NSLog(@"meta == %@ metaIsMeta = %@",meta, @(metaIsMeta));
// Person元类的所属类就是根元类(NSObject MetaClass)
Class meta_meta = object_getClass(meta);
BOOL meta_meta_isMeta = class_isMetaClass(meta_meta);
NSLog(@"meta_meta == %@ meta_metaIsMeta = %@",meta_meta, @(meta_meta_isMeta));
// 根元类的所属类还是它本身,形成闭环
Class meta_meta_meta = object_getClass(meta_meta);
BOOL meta_meta_meta_isMeta = class_isMetaClass(meta_meta_meta);
NSLog(@"meta_meta_meta == %@ meta_meta_meta_isMeta = %@",meta_meta_meta, @(meta_meta_meta_isMeta));
/* super class */
// Man Class的父类是Person Class
Class mSuperCls = [m superclass];
BOOL mSuperClsIsMeta = class_isMetaClass(mSuperCls);
NSLog(@"mSuperCls = %@ mSuperClsIsMeta = %@",mSuperCls, @(mSuperClsIsMeta));
// Person Class的父类是NSObject Class
Class pSuperCls = [mSuperCls superclass];
BOOL pSuperClsIsMeta = class_isMetaClass(pSuperCls);
NSLog(@"pSuperCls = %@ pSuperClsIsMeta = %@",pSuperCls, @(pSuperClsIsMeta));
// 根元类的父类是NSObject Class
Class rootClass = [meta_meta superclass];
const char *rootClsName = class_getName(rootClass);
BOOL rootClassIsMeta = class_isMetaClass(rootClass);
NSLog(@"rootClass == %@ rootClsName = %s rootClassIsMeta = %@",rootClass,rootClsName,@(rootClassIsMeta));
// NSObject Class的父类是nil
Class nSuperCls = [pSuperCls superclass];
BOOL nSuperClsIsMeta = class_isMetaClass(nSuperCls);
NSLog(@"nSuperCls = %@ nSuperClsIsMeta = %@",nSuperCls, @(nSuperClsIsMeta));
上述代码运行结果如下:
Messaging
Runtime的核心就是消息传递,Objective-c中的方法调用都是通过Runtime的消息传递实现的。
整个消息传递的流程:Runtime用objc_msgSend(p, @selector(say:))的方式将say:消息发送给p实例,objc_msgSend函数通过p的isa找到它的Class(Person),在Class(Person)的method_list查找say:,如果没有找到say:,继续往superclass中查找,一旦找到say:,就会执行say:的实现IMP。
消息发送时为了提高查表效率,Runtime建立了缓存机制,消息分发时,会在struct objc_cache \*cache中匹配,如果没有会查方法列表,找到后会加入到缓存中(method_name作为Key,method_imp作为value)。
若果上述流程走完,仍未找到相应的方法,程序一般会抛出异常(unrecognized selector send to …..),但是抛出异常前Runtime会给出三次拯救程序的机会:
1.Method resolution
2.Fast forwarding
3.Normal forwarding
正常的发送流程:
objc_msgSend函数将say:消息发送给p实例,找到say:的实现,执行。
Objective-C代码调用转化为Runtime:
如果正常的消息发送流程出现问题则会依次走下面的逻辑:
Method resolution:
OC运行时会调用+ (BOOL)resolveInstanceMethod:(SEL)sel或者+ (BOOL)resolveClassMethod:(SEL)sel,如果添加了函数并返回YES,运行时会重新启动一次消息发送的过程。
关于class_addMethod(Class cls, SEL name, IMP imp, const char *types)中的types参数(Type Encodings)
Fast forwarding:
如果目标实现了- (id)forwardingTargetForSelector:(SEL)aSelector,Runtime会调用这个方法,把这个消息转发给其他对象的机会。这个方法只要返回的不是nil和self,整个消息发送过程就会重启,发送的对象变成你返回的那个对象,否则,继续Nromal forwarding。
Normal forwarding:
这是Runtime给的最后一次挽救程序的机会,首先会发送- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector消息获得函数的参数和返回值类型。如果返回nil,Runtime则会发出- (void)doesNotRecognizeSelector:(SEL)aSelector消息,这是程序就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送- (void)forwardInvocation:(NSInvocation *)anInvocation消息给目标对象。
NSInvocation实际上就是对一个消息的描述,包括selector以及参数等信息。所以可以在- (void)forwardInvocation:里修改传进来的NSInvocation对象,然后发送- (void)invokeWithTarget:消息给它,传进去一个新的目标:
直接调用msgSend函数发送消息:
//中定义的objc_msgSned,并未明确参数列表和返回值类型,要转化下面的代码才能使用
((void(*)(id, SEL))objc_msgSend)
//然后直接调用发送消息
((void(*)(id, SEL))objc_msgSend)(p,@selector(say:));
绕过消息发送流程,直接调用:
// 定义一个名为saySomething的函数指针
void(*saySomething)(id, SEL,NSString*);
// 将指针指向已有OC方法say:的实现
saySomething = (void(*)(id, SEL,NSString*))[p methodForSelector:@selector(say:)];
// 调用函数
saySomething(p,@selector(say:),@"hello");
(完结,以下无正文)
网友评论