美文网首页
iOS探究 --- Runtime的初探(消息发送解析)

iOS探究 --- Runtime的初探(消息发送解析)

作者: 空空_Jiminy | 来源:发表于2020-04-13 22:46 被阅读0次

    runtime的三种调用方式
    1.runtime api
    2.NSObject api
    3.OC上层方法 @selector

    OC对象

    OC对象的本质是一个结构体。
    由ivars,imp组成。

    方法

    方法的本质是发送消息
    类似于

    void runIMP(id self, SEL _cmd) {
    }
    

    我们常见的sel,是一个方法编号。imp是函数实现的指针,通过SEL找到imp,调用函数。
    那么问题来,sel是怎么找到imp的?

    sel下层通讯

    @selector(myFunc)
    

    经过编译将处理为

    objc_msgSend(myObjc, sel_registerName("myFunc"));
    

    以上为对象方法,还有
    类方法:

    objc_msgSend(objc_getClass("MyClass"), sel_registerName("myFunc"))
    

    父类发送消息:(对象方法)

    struct objc_super mySuper;
    mySuper.receiver = s
    mySuper.super_class = class_getSuperclass(s.class)
    objc_msgSendSuper(&mySuper, @selector(myFunc))
    

    父类发送消息:(类方法)
    struct objc_super myClassSuper;
    myClassSuper.receiver = s.class
    myClassSuper.super_class = class_getSuperclass(object_getClass());
    objc_msgSendSuper(&myClassSuper, sel_registerName("myFunc"));

    类方法

    对象在类里是一个实例,类在元类里也是一个实例。
    所以类方法在元类里,是以对象方法的姿态存在的。

    objc_msgSend的两种方式

    1.快速查找

    在objc_class里
    superclass为父类
    bits为各种数据
    而cache就是一个哈希表。用来存储方法的sel和imp。如果没找到,则走慢速查找。


    image.png

    2.慢速查找

    动态方法解析

    当在运行时实例方法并未实现的时候,动态方法解析就可以闪亮登场了。

    image.png

    上图可以看到,动态解析只会执行一次。

    image.png

    实例方法执行

    _class_resolveInstanceMethod(cls, sel, inst);
    + (BOOL)resolveInstanceMethod:(SEL)sel{
       return [super resolveInstanceMethod:sel];
    }
    

    类方法执行

    _class_resolveInstanceMethod(cls, sel, inst);
    + (BOOL)resolveClassMethod:(SEL)sel{
        return  [super resolveClassMethod:sel];
    }
    

    在实践中,我们会发现resolveInstanceMethod被执行了两次,这是为什么呢?


    isa走位流程.png

    这时候首先需要搞明白isa的走向。
    实例方法 -> 元类 -> 根元类 -> NSObject的实例方法
    可知,实例方法调用了一次,NSObject调用了一次。

    第一次拯救崩溃

    众所周知当调用的方法未实现时,程序会崩溃。
    而在方法动态解析时,runtime提供了一次拯救崩溃的机会。

    #include <objc/runtime.h>
    
    @interface Person : NSObject
    
    - (void)doSomething;
    
    @end
    
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        
        if (sel == @selector(doSomething)) {
            //动态方法解析
            SEL otherSel = @selector(otherThing);
            Method otherMethod = class_getInstanceMethod(self, otherSel);
            IMP otherImp = method_getImplementation(otherMethod);
            const char *type = method_getTypeEncoding(otherMethod);
            return class_addMethod(self, otherSel, otherImp, type);
        }
        
        return [super resolveInstanceMethod:sel];
    }
    
    

    类方法同理,为元类动态增加实例方法。

    第二次拯救崩溃,方法转发流程

    当第一次resolveInstanceMethod执行后,如果消息依然无法进行处理,将会有第二次拯救崩溃的机会,来到消息转发流程。


    消息转发流程.png

    相关文章

      网友评论

          本文标题:iOS探究 --- Runtime的初探(消息发送解析)

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