美文网首页
Objective-C Rumtime-消息机制

Objective-C Rumtime-消息机制

作者: lieon | 来源:发表于2021-03-04 15:42 被阅读0次

    objc_msgSend执行流程

    • OC中的方法调用,其实都是转换为objc_msgSend函数的执行
    • objc_msgSend的执行流程可以分为三大阶段
      • 消息发送
      • 动态方法解析
      • 消息转发
     [person personTest];
    // objc_msgSend(person, @selector(personTest));
    

    objc_msgSend执行流程-源码跟读

    • objc-msg-arm64.s
        END_ENTRY _objc_msgSend
        ENTRY _objc_msgLookup
        UNWIND _objc_msgLookup, NoFrame
    
        cmp x0, #0          // nil check and tagged pointer check
        b.le    LLookup_NilOrTagged //  (MSB tagged pointer looks negative)
        ldr x13, [x0]       // x13 = isa
        and x16, x13, #ISA_MASK // x16 = class  
    
    LLookup_GetIsaDone:
        CacheLookup LOOKUP      // returns imp
          .macro CacheLookup
          .macro CheckMiss
          STATIC_ENTRY __objc_msgSend_uncached
          .macro MethodTableLookup
          __class_lookupMethodAndLoadCache3
    
    • objc-runtime-new.mm
      _class_lookupMethodAndLoadCache3
      lookUpImpOrForward
      getMethodNoSuper_nolock
      search_method_list
      log_and_fill_cache
      _class_resolveMethod
      _objc_msgForward_impcache
      
    • objc-msg-arm64.s
    STATIC_ENTRY __objc_msgForward_impcache
    ENTRY __objc_msgForward
    
    
    IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                           bool initialize, bool cache, bool resolver)
    {
        IMP imp = nil;
        bool triedResolver = NO;
        runtimeLock.assertUnlocked();
        if (cache) {
            imp = cache_getImp(cls, sel);
            if (imp) return imp;
        }
        runtimeLock.read();
    
        if (!cls->isRealized()) {
            runtimeLock.unlockRead();
            runtimeLock.write();
            realizeClass(cls);
            runtimeLock.unlockWrite();
            runtimeLock.read();
        }
        if (initialize  &&  !cls->isInitialized()) {
            runtimeLock.unlockRead();
            _class_initialize (_class_getNonMetaClass(cls, inst));
            runtimeLock.read();
        }
     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();
            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;
    }
    
    // 方法查找的核心算法,采用的是二分查找,在查找前对方法列表进行了排序
    static method_t *findMethodInSortedMethodList(SEL key, const method_list_t *list)
    {
        assert(list);
        const method_t * const first = &list->first;
        const method_t *base = first;
        const method_t *probe;
        uintptr_t keyValue = (uintptr_t)key;
        uint32_t count;
        // 二分查找
        for (count = list->count; count != 0; count >>= 1) {
            probe = base + (count >> 1);
            uintptr_t probeValue = (uintptr_t)probe->name;
            if (keyValue == probeValue) {
                while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
                    probe--;
                }
                return (method_t *)probe;
            }
            if (keyValue > probeValue) {
                base = probe + 1;
                count--;
            }
        }
        return nil;
    }
    
    消息发送的流程
    • 消息发送概括:执行[Person test]代码时,在C++层相当于执行了objc_msgSend(person, @selector(test))函数,objc_msgSend是汇编实现的,在其实现部分,会对reciever(person)进行判断是否为nil,如果是nil,则直接退出,当reciever不为nil时,会进行方法查找,通过实例的对象的isa找到类对象,在类对象中从其类的方法缓存中查找该方法,如果找到了方法就执行,如果没有找到,会从reciever的类对象的方法列表中查找方法,找到了就调用该方法,同时将方法缓存到recieverClass的cache中,如果没有找到会通过superClass指针找到reciever的父类,从父类的cache中查找方法,如果找到了就调用,同时将方法缓存到reciever的类对象的cache中,如果没有找到则从superClass的方法列表中查找方法,如果找到了就调用,同时将方法缓存到reciever的类对象的cache中,如果没有找到就判断是否还有父类,如果有,则重复父类的方法查找流程,如果没有没有,则进行方法解析流程

    动态方法解析

    void _class_resolveMethod(Class cls, SEL sel, id inst)
    {
        if (!cls->isMetaClass()) {
            // try [cls resolveInstanceMethod:sel]
            _class_resolveInstanceMethod(cls, sel, inst);
        } else {
            // try [nonMetaClass resolveClassMethod:sel]
            // and [cls resolveInstanceMethod:sel]
            _class_resolveClassMethod(cls, sel, inst);
            if (!lookUpImpOrNil(cls, sel, inst,  NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) {
                _class_resolveInstanceMethod(cls, sel, inst);
            }
        }
    }
    
    static void _class_resolveClassMethod(Class cls, SEL sel, id inst)
    {
        assert(cls->isMetaClass());
        // 查找 ``SEL_resolveClassMethod``的实现,即+ (BOOL)resolveInstanceMethod:(SEL)sel
        if (!lookUpImpOrNil(cls, SEL_resolveClassMethod, inst, NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) {
            // Resolver not implemented.
            return;
        }
        BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
        bool resolved = msg(_class_getNonMetaClass(cls, inst), SEL_resolveClassMethod, sel);
    
        // Cache the result (good or bad) so the resolver doesn't fire next time.
        // +resolveClassMethod adds to self->ISA() a.k.a. cls
        IMP imp = lookUpImpOrNil(cls, sel, inst, NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
    
        if (resolved && PrintResolving) {
            if (imp) {
                _objc_inform("RESOLVE: method %c[%s %s] "
                             "dynamically resolved to %p", 
                             cls->isMetaClass() ? '+' : '-', 
                             cls->nameForLogging(), sel_getName(sel), imp);
            } else {
                // Method resolver didn't add anything?
                _objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
                             ", but no new implementation of %c[%s %s] was found",
                             cls->nameForLogging(), sel_getName(sel), 
                             cls->isMetaClass() ? '+' : '-', 
                             cls->nameForLogging(), sel_getName(sel));
            }
        }
    }
    
    static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
    {
        // 查找 ``SEL_resolveClassMethod``的实现,即- (BOOL)resolveInstanceMethod:(SEL)sel
        if (!lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,  NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) {
            // Resolver not implemented.
            return;
        }
    
        BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
        bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);
    
        // Cache the result (good or bad) so the resolver doesn't fire next time.
        // +resolveInstanceMethod adds to self a.k.a. cls
        IMP imp = lookUpImpOrNil(cls, sel, inst,  NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
    
        if (resolved  &&  PrintResolving) {
            if (imp) {
                _objc_inform("RESOLVE: method %c[%s %s] "
                             "dynamically resolved to %p", 
                             cls->isMetaClass() ? '+' : '-', 
                             cls->nameForLogging(), sel_getName(sel), imp);
            } else {
                // Method resolver didn't add anything?
                _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                             ", but no new implementation of %c[%s %s] was found",
                             cls->nameForLogging(), sel_getName(sel), 
                             cls->isMetaClass() ? '+' : '-', 
                             cls->nameForLogging(), sel_getName(sel));
            }
        }
    }
    
    动态方法解析流程
    • 动态方法解析概括,当在消息发送阶段没有找到方法具体的实现,会进入动态方法解析阶段,即是在程序运行阶段有没有通过runtime动态添加方法,通过objc的源码发现,动态方法解析包括类对象和实例对象动态方法解析,通过包装resolveInstanceMethod为sel进行一次消息发送,通过调用lookUpImpOrNil找到该方法的实现,就执行,没有找打就进行下一阶段的消息转发

    动态方法的添加

    • 类对象的动态方法添加, 即外部调用的是本类中的类方法
        void c_other(id self, SEL _cmd)
      {
        NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
      }
      
      + (BOOL)resolveClassMethod:(SEL)sel
      {
          if (sel == @selector(test)) {
            // 第一个参数是object_getClass(self)获取打的是元类对象,在类方法中self,代表类对象,类对象的isa指针获取到的是元类对象
            class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
            return YES;
        }
        return [super resolveClassMethod:sel];
      }
      
      + (void)other
      {
          NSLog(@"%s", __func__);
      }
      
      + (BOOL)resolveClassMethod:(SEL)sel {
          if (sel == @selector(test)) {
            // 获取其他方法 第一个参数是object_getClass(self)获取的是元类对象,在类方法中self,代表类对象,类对象的isa指针获取到的是元类对象
            Method method = class_getInstanceMethod(object_getClass(self), @selector(other));
            // 动态添加test方法的实现
            class_addMethod(object_getClass(self), sel,
                            method_getImplementation(method),
                            method_getTypeEncoding(method));
            return YES;
        }
        return [super resolveClassMethod:sel];
      }
      
      
    • 实例对象的动态方法添加,即外部是通过实例对象调用的方法
    - (void)other
    {
        NSLog(@"%s", __func__);
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        if (sel == @selector(test)) {
            // 获取其他方法
            Method method = class_getInstanceMethod(self, @selector(other));
            // 动态添加test方法的实现
            class_addMethod(self, sel,method_getImplementation(method), method_getTypeEncoding(method));
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        if (sel == @selector(test)) {
            // 获取其他方法
            struct method_t *method = (struct method_t *)class_getInstanceMethod(self, @selector(other));
    
            // 动态添加test方法的实现
            class_addMethod(self, sel, method->imp, method->types);
    
            // 返回YES代表有动态添加方法
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    
    • @dynamic是告诉编译器不用自动生成getter和setter的实现,等到运行时再添加方法实现

    消息转发

    • 转发给其他对象来执行该方法
    - (id)forwardingTargetForSelector:(SEL)aSelector
    {
        if (aSelector == @selector(test)) {
            // 转发给MJCat对象来执行test方法,相当于使用objc_msgSend对cat对象发送消息 objc_msgSend([[MJCat alloc] init], aSelector)
            return [[MJCat alloc] init];
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    
    • 当没有其他对象来接收改方法,则进行方法签名,在forwardInvocation中执行
      // 方法签名:返回值类型、参数类型
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    {
        if (aSelector == @selector(test)) {
            return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
        }
        return [super methodSignatureForSelector:aSelector];
    }
    
    // NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
    //    anInvocation.target 方法调用者
    //    anInvocation.selector 方法名
    //    [anInvocation getArgument:NULL atIndex:0]
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
    //    anInvocation.target = [[MJCat alloc] init];
    //    [anInvocation invoke];
    
        [anInvocation invokeWithTarget:[[MJCat alloc] init]];
        
    }
    
    • forwardingTargetForSelector methodSignatureForSelector forwardInvocation都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-)

      消息转发流程
    • 消息转发概括:在动态方法解析阶段,没有找到合适的方法实现,将会进行消息转发, 会先调用forwardingTargetForSelector,判断是否有其他对象来执行该方法(其他对象内部已经实现了改方法),如果有则使用objc_msgSend执行消息发送,如果没有,则会执行methodSignatureForSelector对将要执行的方法进行签名,签名包括:返回值类型,参数类型,使用的是Type Encoding来进行的类型编码,在forwardInvocation中执行相应的逻辑,该方法可以实现任何逻辑,如果没有签名,则会调用desNotRecognizeSelector方法,抛出异常

    相关文章

      网友评论

          本文标题:Objective-C Rumtime-消息机制

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