美文网首页
关于iOS中Runtime的理解

关于iOS中Runtime的理解

作者: 程序后媛团 | 来源:发表于2018-02-05 18:58 被阅读0次

虽说苹果公司早就公开了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");

(完结,以下无正文)

相关文章

  • 关于iOS中Runtime的理解

    虽说苹果公司早就公开了Objective-C的Runtime源码,然而我却并没有深入了解过Runtime,实在汗颜...

  • 赵熊猫关于iOS的文章索引

    1.Runtime的个人理解 2.Runloop的个人理解 3.iOS - 关于UIView中添加手势监听和tab...

  • Java中的反射

    反射 今天我来分享下, 我关于Java中反射的理解。如果做过iOS开发的同学应该很清楚iOS里Runtime的黑魔...

  • iOS~runtime

    iOS~runtime理解iOS runtime学习笔记Objective-C Runtime让你快速上手Runt...

  • 关于ios runtime理解

    什么是runtime 说到runtime,根据字面意思就是运行期间。但我觉得首先应该说一下oc到底是个什么东西。首...

  • ios runtime的使用

    ios 开发中的runtime 的简单理解 1.runtime也就是ios开发中的运行时机制,是一套比较底层的C语...

  • 多线程cocoachina资源

    iOS:学习runtime的理解和心得

  • iOS中的runtime理解

    Runtime的使用:1.动态获取/创建类2.动态为一个类增加属性(关联对象)或方法3.在程序运行过程中遍历类中的...

  • IOS学习笔记--RunTime的理解

    IOS学习笔记--RunTime的理解 RunTime的理解 runtime:运行时刻是指一个程序在运行(或者在被...

  • 壹、面试复习OC篇之runtime

    暂时copy过来,过后添加自己理解 原文地址:iOS-runtime通篇详解-上 iOS-runtime通篇详解-...

网友评论

      本文标题:关于iOS中Runtime的理解

      本文链接:https://www.haomeiwen.com/subject/umlhsxtx.html