初探OC底层原理之方法决议

(一)动态方法决议

  • 案例如下 创建一个LGTeacher类,声明一个say666方法,方法不实现
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGTeacher *p = [LGTeacher alloc];
        [LGTeacher say666];
    }
    return 0;
}
image.png
  • unrecognized selector 经典的方法为实现奔溃,我们没有写log 为什么会有这样的输出呢?

  • 在方法慢速查找我们找到相关代码


    image.png
  • 这里找到给forward_imp 一个定语赋值,当慢速查找父类为nil时


    image.png
  • 当慢速查找没有找到时会进入_bjc_msgForward_impcache 方法决议

    STATIC_ENTRY __objc_msgForward_impcache

    // No stret specialization.
    b   __objc_msgForward

    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
  • 从源码中找到x17,进入 __objc_forward_handler 方法判断
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;
  • 从_objc_forward_handler 方法中终于找到日志实现的地方

  • 思考:????当返回unrecognized selector 之前 lookUpImpOrForward 里面有没做其他操作?

  • 在探究慢速查找流程lookUpImpOrForward中,如果没有查找到imp就会走动态方法决议流程resolveMethod_locked

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior){
...

    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }
  • 从上面判断逻辑可以得出resolveMethod_locked 只会进入一次 我们看下resolveMethod_locked 里面做了什么??
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();
    // 给你一次机会 拯救地球 -> imp
    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
            resolveInstanceMethod(inst, sel, cls);
        }
    }
    
    // chances are that calling the resolver have populated the cache
    // so attempt using it
    return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}
  • return lookUpImpOrForwardTryCache(inst, sel, cls, behavior); 从这句代码可以得出resolveMethod_locked是做了一次容错,从新给一次拯救机会

  • resolveInstanceMethod 是非元类实现的方法(实例方法),resolveClassMethod元类方法(类方法)

static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);

    if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, sel);
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    ASSERT(cls->isMetaClass());

    if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
        // Resolver not implemented.
        return;
    }

    Class nonmeta;
    {
        mutex_locker_t lock(runtimeLock);
        nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
        // +initialize path should have realized nonmeta already
        if (!nonmeta->isRealized()) {
            _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
                        nonmeta->nameForLogging(), nonmeta);
        }
    }
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(nonmeta, @selector(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 = lookUpImpOrNilTryCache(inst, sel, cls);

    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));
        }
    }
}
  • BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, sel);

  • BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);

  • 这2个方法都调用 objc_msgSend 向系统发送消息

  • 在LGTeacher类中添加resolveInstanceMethod方法

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSLog(@"resolveInstanceMethod :%@-%@",self,NSStringFromSelector(sel));
    return [super resolveInstanceMethod:sel];
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGTeacher *p = [LGTeacher alloc];
        [p say666];
}
    return 0;
}
  • lldb输出为
2021-07-12 17:37:18.775526+0800 KCObjcBuild[7443:173330] resolveInstanceMethod :LGTeacher-say666
2021-07-12 17:37:18.776380+0800 KCObjcBuild[7443:173330] resolveInstanceMethod :LGTeacher-say666
2021-07-12 17:37:18.776749+0800 KCObjcBuild[7443:173330] -[LGTeacher say666]: unrecognized selector sent to instance 0x1011405d0
  • 从输出可以看出resolveInstanceMethod调用了2次 为什么呢
image.png
  • 从图看出第一调用发起者是resolveMethod_locked 可以看到走的是慢速查找流程的动态决议方法


    image.png
  • 上图可以看出第二次 发起者CoreFoundation`-[NSObject(NSObject) methodSignatureForSelector:]:

  • 根据上面分析得出动态消息决议流程图

image.png

你可能感兴趣的:(初探OC底层原理之方法决议)