美文网首页
iOS 消息机制(一)

iOS 消息机制(一)

作者: 水煮杰尼龟 | 来源:发表于2020-09-12 14:45 被阅读0次

      我们都知道当我们用[]调用方法时,编译器会修改为objc_msgSend
    这个我们可以clang看看就知道了。

    person new
    clang person new
    而根据具体情况,编译器会将消息发送修改为四种情况之一
    objc_msgSend
    objc_msgSend_stret
    objc_msgSendSuper
    objc_msgSendSuper_stret

    比如我们调用[super xxx]的时候,则会改为objc_msgSendSuper的形式,而带有stret,表明方法返回值是一个结构体类型。

    objc_msgSend

    通过源码来看,objc_msgSend 使用汇编写的,脑袋瓜子嗡嗡的。
    结合注释,网上资料简单捋捋,看一下arm64里的汇编源码,基于objc4-779.1源码

    ENTRY _objc_msgSend
        UNWIND _objc_msgSend, NoFrame
      /// 检测receiver是否为nil。如果为nil,则进入LNilOrTagged
        cmp p0, #0          // nil check and tagged pointer check
    #if SUPPORT_TAGGED_POINTERS
        b.le    LNilOrTagged        //  (MSB tagged pointer looks negative)
    #else
        b.eq    LReturnZero
    #endif
    /// 如果不为nil,则现将receiver的isa存入p13;
        ldr p13, [x0]       // p13 = isa
    /// 取出isa中的class,放到p16中
        GetClassFromIsa_p16 p13     // p16 = class
    LGetIsaDone:
        // calls imp or objc_msgSend_uncached
    /// 调用CacheLookup NORMAL 方法内部查找cache,如果未命中,则进入objc_msgSend_uncached
        CacheLookup NORMAL, _objc_msgSend
    
    #if SUPPORT_TAGGED_POINTERS
    LNilOrTagged:
        b.eq    LReturnZero     // nil check
    
        // tagged
        adrp    x10, _objc_debug_taggedpointer_classes@PAGE
        add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
        ubfx    x11, x0, #60, #4
        ldr x16, [x10, x11, LSL #3]
        adrp    x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
        add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
        cmp x10, x16
        b.ne    LGetIsaDone
    
        // ext tagged
        adrp    x10, _objc_debug_taggedpointer_ext_classes@PAGE
        add x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
        ubfx    x11, x0, #52, #8
        ldr x16, [x10, x11, LSL #3]
        b   LGetIsaDone
    // SUPPORT_TAGGED_POINTERS
    #endif
    
    LReturnZero:
        // x0 is already zero
        mov x1, #0
        movi    d0, #0
        movi    d1, #0
        movi    d2, #0
        movi    d3, #0
        ret
    
        END_ENTRY _objc_msgSend
    
    未命中缓存即进入objc_msgSend_uncached,来看看
    STATIC_ENTRY __objc_msgSend_uncached
        UNWIND __objc_msgSend_uncached, FrameWithNoSaves
    
        // THIS IS NOT A CALLABLE C FUNCTION
        // Out-of-band p16 is the class to search
        
        MethodTableLookup
        TailCallFunctionPointer x17
    
        END_ENTRY __objc_msgSend_uncached
    

    objc_msgSend_uncached内部调用MethodTableLookup,而MethodTableLookup里会调用lookUpImpOrForward 寻找classIMP实现或进行消息转发。

    lookUpImpOrForward 进入正题
    IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
    {
        const IMP forward_imp = (IMP)_objc_msgForward_impcache;
        IMP imp = nil;
        Class curClass;
        
        runtimeLock.assertUnlocked();
    
        // Optimistic cache lookup
        // 先在cache中查找imp,找到就去 done_nolock,返回imp
        if (fastpath(behavior & LOOKUP_CACHE)) {
            imp = cache_getImp(cls, sel);
            if (imp) goto done_nolock;
        }
    
        runtimeLock.lock();
    
        if (slowpath(!cls->isRealized())) {
            // 如果class没有被relize,先relize
            cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
            // runtimeLock may have been dropped but is now locked again
        }
    
        if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
            // 如果class没有init,则先init
            cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
        }
    
        runtimeLock.assertLocked();
        curClass = cls;
    
        /// 沿着继承链向上寻找
        for (unsigned attempts = unreasonableClassCount();;) {
            // curClass method list.
            /// 在当前class的method list中查找有无imp // 在class的方法列表methods中,根据SEL查找对应的imp
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                imp = meth->imp;
                /// 找到了 去done ,将imp存储到当前class 的cache中
                goto done;
            }
    
            if (slowpath((curClass = curClass->superclass) == nil)) {/// 无父类
                // No implementation found, and method resolver didn't help.
                // Use forwarding.
                ///找不到实现,方法解析器也没有帮助。
                /// 使用转发imp。
                imp = forward_imp;
                break;
            }
    
            // Halt if there is a cycle in the superclass chain.
            if (slowpath(--attempts == 0)) {
                _objc_fatal("Memory corruption in class list.");
            }
    
            // Superclass cache.
            // 找super class的cache
            imp = cache_getImp(curClass, sel);
            if (slowpath(imp == forward_imp)) {
                /// imp 是消息转发imp。停止搜索,返回imp
                break;
            }
            if (fastpath(imp)) {
                ///找到了 去done ,将imp存储到当前class 的cache中
                goto done;
            }
        }
    
        // No implementation found. Try method resolver once.
    
        /// 在class和其所有的super class,均未找到imp ,进入动态方法解析流程resolveMethod
        if (slowpath(behavior & LOOKUP_RESOLVER)) {
            behavior ^= LOOKUP_RESOLVER;
            return resolveMethod_locked(inst, sel, cls, behavior);
        }
    
     done:
        log_and_fill_cache(cls, imp, sel, inst, curClass);
        runtimeLock.unlock();
     done_nolock:
        if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
            return nil;
        }
        return imp;
    }
    

    总结一下大致流程

    1. 先在cache中查找imp,找到了返回imp
    2. 在当前classmethod list中查找有无imp , 在class的方法列表methods中,根据SEL查找对应的imp
    3. 找到了 ,将imp存储到当前classcache
    4. class的所有super classes中查找imp(先看Super classcache,再看super class的方法列表)
    5. 找到了,同3
    6. 均未找到imp,进入动态方法解析流程resolveMethod
    接下来也看看objc_msgSendSuper

    先看看它的定义

    OBJC_EXPORT id _Nullable
    objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
        OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
    

    可以看到传入的是一个objc_super结构体,结构如下:

    struct objc_super {
        /// Specifies an instance of a class.
        __unsafe_unretained _Nonnull id receiver;
    
        __unsafe_unretained _Nonnull Class super_class;
        /* super_class is the first class to search */
    };
    

    receiver:消息接收者,是当前class
    super_class :父类,从父类开始寻找方法,而略过当前类
    所以 调用[super xxxx] 并不是说父类来调用方法,只是从父类开始寻找方法,然后发送到当前class,最终作用对象,还是当前class
    比如当我调用父类的方法时,clang来看看

    objc_msgSendSuper

    可以看到确实如上所述,objc_super 结构体的 receiver 就是传的self,即当前class, 而super_class 则是当前class的父类。
    这也就是下面的经典面试题,输出为什么一致的原因了。(都输出[self class]

    @interface Father : NSObject
    @end
    
    @implementation Father
    @end
    
    @interface Son : Father
    - (void)showClass;
    @end
    
    @implementation Son
    - (void)showClass {
        NSLog(@"self class = %@, super class = %@", [self class], [super class]);
    }
    
    ...
    Son *son = [Son new];
    [son showClass];    // 这里输出什么?
    ...
    
    看看arm64 的汇编源码
    ENTRY _objc_msgSendSuper
        UNWIND _objc_msgSendSuper, NoFrame
    
        ldp p0, p16, [x0]       // p0 = real receiver, p16 = class
        // calls imp or objc_msgSend_uncached
        CacheLookup NORMAL, _objc_msgSendSuper
    
        END_ENTRY _objc_msgSendSuper
    

    这里面的p0 = real receiver 即是当前class, 而p16 = classclass即为父类,
    后面的流程 与 objc_msgSend是一致的,只不过lookUpImpOrForward 传入的cls是父类了

    下一篇再捋 动态方法解析resolveMethod
    end

    相关文章

      网友评论

          本文标题:iOS 消息机制(一)

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