iOS底层

objc_msgSend分析(2)-方法查找

2020-01-03  本文已影响0人  xxxxxxxx_123

  上节我们分析了objc_msgSend的快速查找部分(汇编部分),这一节我们来分析其慢速部分,也就是c语言部分。

方法查找流程

首先我们实现以下代码:

@interface TPerson : NSObject
- (void)sayNB;
+ (void)sayHappay;
@end

@interface TStudent : TPerson
- (void)sayHello;
+ (void)sayObjc;
@end

@interface NSObject (Category)
- (void)sayMaster;
+ (void)sayEasy;
@end

以上方法均在.m文件中实现。

一、实例方法的查找流程

当我们调用以下代码的时候:

TStudent *stu = [[TStudent alloc] init];
[stu sayHello];
[stu sayNB];
[stu sayMaster];
[stu performSelector: @selector(sayHeHe)];

我们会发现前三个方法均会调用成功,但是sayHeHe会崩溃,由此,我们可以知道:

一、类方法的查找流程

[TStudent sayHappay];
[TStudent sayObjc];
[TStudent sayEasy];
[TStudent performSelector: @selector(sayMaster)];
[TStudent performSelector: @selector(sayNB)];
[TStudent performSelector: @selector(sayHeHe)];

运行以上代码,我们会发现前四个方法会正常执行,而sayNB、sayHeHe会崩溃,由此,可以得出:

接上一节objc_msgSend汇编流程结束之后,我们进入下列C方法:

lookUpImpOrForward(cls, sel, obj, 
                   YES/*initialize*/,
                   NO/*cache*/, 
                   YES/*resolver*/)
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    IMP imp = nil;
    bool triedResolver = NO;

    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.lock();
    checkIsKnownClass(cls);

    // 准备条件,将当前类、父类的相关信息罗列出列出来
    if (!cls->isRealized()) {
        realizeClass(cls);
    }

    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlock();
        _class_initialize (_class_getNonMetaClass(cls, inst));
        runtimeLock.lock();
        // 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.assertLocked();

    // Try this class's cache.

    imp = cache_getImp(cls, sel);
    if (imp) goto done;

    // Try this class's method lists.
    {
    // 从当前类的methodList里面去查找方法
        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
            imp = cache_getImp(curClass, sel);
            if (imp) {
                // 缓存命中的话,如果不是 _objc_msgForward_impcache
                if (imp != (IMP)_objc_msgForward_impcache) {
                    // Found the method in a superclass. Cache it in this class.
                    // 写入当前类的缓存中,然后发返回imp
                    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 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.unlock();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.lock();
        // 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.unlock();

    return imp;
}
static void checkIsKnownClass(Class cls)
{
    if (!isKnownClass(cls))
        _objc_fatal("Attempt to use unknown class %p.", cls);
}

cache_getImp 则是返回汇编的查找部分,用汇编实现

    STATIC_ENTRY _cache_getImp

    GetClassFromIsa_p16 p0
    CacheLookup GETIMP

LGetImpMiss:
    mov p0, #0
    ret

    END_ENTRY _cache_getImp

__objc_msgForward_impcache 是汇编实现:

END_ENTRY __objc_msgForward_impcache
    ENTRY __objc_msgForward

    adrp    x17, __objc_forward_handler@PAGE
    ldr p17, [x17, __objc_forward_handler@PAGEOFF]
    TailCallFunctionPointer x17
    
    END_ENTRY __objc_msgForward

抛出异常:

objc_defaultForwardHandler(id self, SEL sel)
{
    _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
                "(no message forward handler is installed)", 
                class_isMetaClass(object_getClass(self)) ? '+' : '-', 
                object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
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);
        }
    }
}

以上代码可以总结为以下的流程图:

image

处理unrecognized selector sent to instance报错

  当我们找不到某一个方法的定义/实现的时候,系统帮我们实现了resolveInstanceMethod作为补救,我们可以在该方法里面实现对崩溃的拦截和进一步处理:

- (void)showUpdateTips {
    NSLog(@"-----更新-------");
}

// 动态解析没有处理
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    IMP updateImp = class_getMethodImplementation(self, @selector(showUpdateTips));
    Method updateMethod = class_getInstanceMethod(self, @selector(showUpdateTips));
    const char *type = method_getTypeEncoding(updateMethod);
    return class_addMethod(self, sel, updateImp, type);
}

总结:
  objc_msgSend进入c语言部分的时候,需要先判断当前类是否是isKnownClass;如果是则继续判断是否是isRealized(),如果不是realizeClass;接着判断类是否初始化,如果没有则进行初始化操作;然后从当前类的缓存获取imp,如果缓存命中则调用imp;没有命中则去类的方法列表里寻找;如果找到就写入缓存并调用;找不到就逐层级遍历当前类的父类;在父类的中寻找和当前类中寻找的原理一样,就不一一赘述;如果在所有父类种还没找到,则调用一次_class_resolveMethod(cls, sel, inst)看看是否进行动态解析处理,然后再执行一次方法查找,如果在遍历结束还是未找到,则会抛出异常unrecognized selector sent to instance。

上一篇下一篇

猜你喜欢

热点阅读