美文网首页iOS进阶
07.1-OC中Runtime消息机制

07.1-OC中Runtime消息机制

作者: 光强_上海 | 来源:发表于2020-06-21 17:18 被阅读0次

    我们都知道OC中的方法调用,最终底层都是调用runtime的objc_msgSend()API来发送消息,也就是OC的消息机制消息机制的执行流程又分下面的三个阶段,当执行第一个阶段时方法不能调用成功变会进入第二个阶段,如果第二个阶段还无法处理消息,则进入第三个阶段消息转发,如果第三个阶段还不能处理消息,则程序就会抛出异常。

    • 第一阶段:消息发送
    • 第二阶段:动态方法解析
    • 第三阶段:消息转发

    消息发送阶段

    我们创建一个新工程,新建一个Person类,代码如下:

    Person

    @interface Person : NSObject
    
    - (void)test;
    @end
    
    
    @implementation Person
    
    - (void)test {
        NSLog(@"%s", __func__);
    }
    @end
    

    main函数

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            
            Person *person = [[Person alloc] init];
            [person test];
        }
        return 0;
    }
    

    然后我们执行命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.mmain.m文件转换为c++代码如下:

    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
    
            Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
            ((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("test"));
    
        }
        return 0;
    }
    

    我们通过转换的c++代码可以看到,当我们进行方法调用时,底层代码就转化为runtime的objc_msgSend函数,objc_msgSend()函数接受两个参数,具体解释如下:

        Person *person = [[Person alloc] init];
        
        // 当执行OC代码`[person test]`转换为底层c++代码就是`objc_msgSend(person, sel_registerName("test"))`
        [person test];
            
        /**
         person:消息接收者(receiver),也就是objc_msgSend()函数给消息接收者(receiver)发送一条消息(消息名称为sel_registerName("test"))
         sel_registerName("test"):消息名称(方法名)
         */
        objc_msgSend(person, sel_registerName("test"));
        
        需要注意:OC的`@selector(test)`转换为底层代码就是`sel_registerName("test")`
    

    消息发送阶段查找方法流程如图:

    image

    动态方法解析阶段(也就是通过runtime动态添加一个方法)

    我们创建一个Person2类来演示第二阶段,具体代码如下:

    Person2

    @interface Person2 : NSObject
    
    - (void)instanceTest;
    
    + (void)classTest;
    @end
    
    
    @implementation Person2
    
    void Ctest(id self, SEL _cmd) {
        NSLog(@"C语言函数");
    }
    
    - (void)test2 {
        NSLog(@"%s", __func__);
    }
    
    + (void)test3 {
        NSLog(@"%s", __func__);
    }
    
    // 解析类方法
    + (BOOL)resolveClassMethod:(SEL)sel {
        if (sel == @selector(classTest)) {
            // 这个test3方法要在元类对象中查找,所以使用object_getClass(self)
            Method method = class_getInstanceMethod(object_getClass(self), @selector(test3));
            
            // 动态添加一个类方法,因为类方法需要在元类对象中查找,所以使用object_getClass(self)
            class_addMethod(object_getClass(self), sel, method_getImplementation(method), method_getTypeEncoding(method));
            
    //        class_addMethod(object_getClass(self), sel, (IMP)Ctest, "v16@0:8");
            
            // 如果实现了方法解析,则返回YES
            return YES;
        }
        return [super resolveClassMethod:sel];
    }
    
    // 解析实例方法
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        // 这里做下判断,只处理方法instanceTest
        if (sel == @selector(instanceTest)) {
            // 创建一个Method对象,Method对象的底层结构就是method_t
            Method method = class_getInstanceMethod(self, @selector(test2));
    
            // 动态添加一个实例方法
            class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
            
            // 我们也可以动态添加C言语函数
    //        class_addMethod(self, sel, (IMP)Ctest, "v16@0:8");
            
            // 如果实现了方法解析,则返回YES
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    @end
    

    main函数

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person2 *person = [[Person2 alloc] init];
            [person instanceTest];
            
            [Person2 classTest];
        }
        return 0;
    }
    

    第二阶段动态方法解析阶段的核心函数

    • resolveInstanceMethod,用来处理实例方法
    • resolveClassMethod,用来处理类方法

    动态解析方法阶段流程如图:

    image

    消息转发阶段

    我们创建一个Person3类来演示第三阶段,具体代码如下:

    Person3

    @interface Person3 : NSObject
    
    - (void)instanceTest;
    
    + (void)classTest;
    @end
    
    
    @implementation Person3
    
    #pragma mark - 处理类方法消息转发
    
    // 处理类方法消息转发
    //+ (id)forwardingTargetForSelector:(SEL)aSelector {
    //    if (aSelector == @selector(classTest)) {
    //        // 返回一个对象,将消息转发给这个对象来处理,最终底层执行了objc_msgSend([[Student alloc] init], aSelector)
    //        return [Student class];
    //    }
    //
    //    return [super forwardingTargetForSelector:aSelector];
    //}
    
    // 方法签名
    //+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    //    if (aSelector == @selector(classTest)) {
    //        return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
    //    }
    //    return [super methodSignatureForSelector:aSelector];
    //}
    
    // 最后一次机会处理消息,这里可以灵活处理消息
    //+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    //    NSLog(@"灵活处理消息转发-begin");
    //
    //    [anInvocation invokeWithTarget:[Student class]];
    //
    //    NSLog(@"灵活处理消息转发-end");
    //}
    
    
    #pragma mark - 处理实例方法消息转发
    
    //- (id)forwardingTargetForSelector:(SEL)aSelector {
    //    if (aSelector == @selector(instanceTest)) {
    //        // 返回一个对象,将消息转发给这个对象来处理,最终底层执行了objc_msgSend([[Student alloc] init], aSelector)
    //        return [[Student alloc] init];
    //    }
    //
    //    return [super forwardingTargetForSelector:aSelector];
    //}
    
    // 方法签名
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        if (aSelector == @selector(instanceTest)) {
            return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
        }
        return [super methodSignatureForSelector:aSelector];
    }
    
    // 最后一次机会处理消息,这里可以灵活处理消息,比在forwardingTargetForSelector:函数中处理消息转发更加的灵活多变
    - (void)forwardInvocation:(NSInvocation *)anInvocation {
        NSLog(@"灵活处理消息转发-begin");
    
        [anInvocation invokeWithTarget:[[Student alloc] init]];
    
        NSLog(@"灵活处理消息转发-end");
    }
    @end
    

    main函数

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person3 *person = [[Person3 alloc] init];
            [person instanceTest];
            
    //        [Person3 classTest];
        }
        return 0;
    }
    

    消息转发阶段有三个核心函数,注意:这三个函数都有与之对应的类方法实现

    • forwardingTargetForSelector:
    • methodSignatureForSelector:
    • forwardInvocation:

    这里需要注意:forwardingTargetForSelector:函数没有返回处理消息的对象时,程序就会执行methodSignatureForSelector:函数,当methodSignatureForSelector:返回了正确方法签名后才会执行最后的处理函数forwardInvocation:

    消息转发阶段流程如图:

    image

    消息机制底层源码查找路径:objc4源码 -> objc-runtime-new.mm -> lookUpImpOrForward()

    lookUpImpOrForward核心源码函数入口,在此函数源码中详细包含了消息机制的三大阶段

    IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                           bool initialize, bool cache, bool resolver)
    {
        IMP imp = nil;
        bool triedResolver = NO;
    
        
        /*----------runtime消息机制的第一阶段:消息发送阶段-----------*/
        
        
        runtimeLock.assertUnlocked();
        
        /************************* 查找方法流程 **************************/
    
        // Optimistic cache lookup
        if (cache) {
            // 如果缓存中有方法,则从方法缓存中查找
            imp = cache_getImp(cls, sel);
            if (imp) return imp;
        }
    
        // runtimeLock is held during isRealized and isInitialized checking
        // to prevent races against concurrent realization.
    
        // runtimeLock is held during method search to make
        // method-lookup + cache-fill atomic with respect to method addition.
        // Otherwise, a category could be added but ignored indefinitely because
        // the cache was re-filled with the old value after the cache flush on
        // behalf of the category.
    
        runtimeLock.read();
    
        if (!cls->isRealized()) {
            // Drop the read-lock and acquire the write-lock.
            // realizeClass() checks isRealized() again to prevent
            // a race while the lock is down.
            runtimeLock.unlockRead();
            runtimeLock.write();
    
            realizeClass(cls);
    
            runtimeLock.unlockWrite();
            runtimeLock.read();
        }
    
        // 在objc_msgSend发送消息前,先判断下这个类有没有被初始化
        if (initialize  &&  !cls->isInitialized()) {
            runtimeLock.unlockRead();
            
            // 没有初始化,就来初始化这个类
            _class_initialize (_class_getNonMetaClass(cls, inst));
            runtimeLock.read();
            // If sel == initialize, _class_initialize will send +initialize and 
            // then the messenger will send +initialize again after this 
            // procedure finishes. Of course, if this is not being called 
            // from the messenger then it won't happen. 2778172
        }
    
        
     retry:    
        runtimeLock.assertReading();
    
        // Try this class's cache.
        
        // 如果缓存中找到方法的实现,则返回到汇编
        imp = cache_getImp(cls, sel);
        if (imp) goto done;
    
        // 如果方法缓存列表中没有找到方法,则在当前类对象的方法列表中进行查找
        // Try this class's method lists.
        {
            Method meth = getMethodNoSuper_nolock(cls, sel);
            
            // 找到了方法
            if (meth) {
                
                // 将找到的方法添加到方法缓存列表中
                log_and_fill_cache(cls, meth->imp, sel, inst, cls);
                imp = meth->imp;
                goto done;
            }
        }
    
        
        // 如果当前类对象的方法列表中也没有找到,则去父类中查找
        // Try superclass caches and method lists.
        {
            unsigned attempts = unreasonableClassCount();
            
            // 通过cls->superclass找到父类对象,循环遍历查找是否还有上级父类
            for (Class curClass = cls->superclass;
                 curClass != nil;
                 curClass = curClass->superclass)
            {
                // Halt if there is a cycle in the superclass chain.
                if (--attempts == 0) {
                    _objc_fatal("Memory corruption in class list.");
                }
                
                // 查找父类的方法列表前,也是先查找缓存列表
                // Superclass cache.
                imp = cache_getImp(curClass, sel);
                if (imp) {
                    if (imp != (IMP)_objc_msgForward_impcache) {
                        
                        // 在父类缓存中找到了,将其缓存到当前类对象中
                        // Found the method in a superclass. Cache it in this class.
                        log_and_fill_cache(cls, imp, sel, inst, curClass);
                        goto done;
                    }
                    else {
                        // Found a forward:: entry in a superclass.
                        // Stop searching, but don't cache yet; call method 
                        // resolver for this class first.
                        break;
                    }
                }
                
                // 缓存列表中没有找到,则在父类的方法列表中查找
                // Superclass method list.
                Method meth = getMethodNoSuper_nolock(curClass, sel);
                if (meth) {
                    
                    // 找到则将方法添加到缓存列表
                    log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                    imp = meth->imp;
                    goto done;
                }
            }
        }
    
        
        /*----------这里需要注意,当消息发送阶段没有找到对应方法,则进入第二阶段:消息解析-----------*/
        
        
        // No implementation found. Try method resolver once.
    
        // 之前没有尝试解析过,就进入解析阶段
        if (resolver  &&  !triedResolver) {
            runtimeLock.unlockRead();
            
            // 处理消息解析
            _class_resolveMethod(cls, sel, inst);
            runtimeLock.read();
            // Don't cache the result; we don't hold the lock so it may have 
            // changed already. Re-do the search from scratch instead.
            triedResolver = YES;
            goto retry;
        }
    
        /*----------当消息解析阶段仍不能处理方法,则进入第三阶段:消息转发---------------*/
        
        // No implementation found, and method resolver didn't help. 
        // Use forwarding.
    
         // 消息转发
        imp = (IMP)_objc_msgForward_impcache;
        
        // 消息转发完成后,将方法添加到缓存列表
        cache_fill(cls, sel, imp, inst);
    
     done:
        runtimeLock.unlockRead();
    
        return imp;
    }
    

    讲解示例Demo地址:https://github.com/guangqiang-liu/07.1-RunTimeMessageSend

    更多文章

    相关文章

      网友评论

        本文标题:07.1-OC中Runtime消息机制

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