iOS 消息机制(一)

2020-09-12  本文已影响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
上一篇下一篇

猜你喜欢

热点阅读