美文网首页iOS底层
iOS底层-动态方法决议与消息转发

iOS底层-动态方法决议与消息转发

作者: 含笑州 | 来源:发表于2020-09-25 11:12 被阅读0次

动态方法决议

我们全局搜索lookUpImpOrForward,最后在objc-runtime-new.mm文件中找到了源码实现,这是一个c实现的函数,源码如下:


图1 图2

我们发现源码中有个resolveMethod_locked,点击进入发现源码如下:

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();
    // 动态方法决议 : 给一次机会 重新查询
    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 (!lookUpImpOrNil(inst, sel, cls)) {  // 为什么要有这行代码
            resolveInstanceMethod(inst, sel, cls);
        }
    }

    // chances are that calling the resolver have populated the cache
    // so attempt using it
    return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}

我们发现如果是元类的话就走resolveClassMethod,不是元类的话就走resolveInstanceMethod,接下来代码分析

代码分析

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        LGPerson *person = [LGPerson alloc];
        [LGPerson sayNB];

    }
    return 0;
}

如果这个方法没有实现,会报错误如下:


错误图

实现方案

我们在LGPerson这个类中实现如下代码:

+ (BOOL)resolveClassMethod:(SEL)sel{
    NSLog(@"%@ 来了",NSStringFromSelector(sel));
    if (sel == @selector(sayNB)) {

        IMP imp           = class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
        Method sayMMethod = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(lgClassMethod));
        const char *type  = method_getTypeEncoding(sayMMethod);
        return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);
    }
    return [super resolveClassMethod:sel];
}

其实我们之前的探索以及isa的走位图,我们可以发现类方法存在元类中,实际上也是元类中的实例方法。

消息转发机制

图1

我们通过图1可以发现在执行doesNotRecognizedSelector之前,执行forwarding_prep_0forwarding
那么接下我们要去寻找forwarding_prep_0
forwarding,我们下载CoreFoundation开源了的代码里面查找,发现找不到。
接下来通过image list指令查看所有的编译文件
我们找到CoreFoundation的编译文件路径:/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation

图2

接下来我们通过hopper软件对这个CoreFoundation编译文件进行反汇编


图3
图4

forwarding的伪代码里面,我们看到:
先找forwardingTargetForSelector,
如果找到forwardingTargetForSelector,消息转发
如果没有找到forwardingTargetForSelector,就会找methodSignatureForSelector
如果没有找到methodSignatureForSelector,直接unrecognized selector
如果找到methodSignatureForSelector,会继续寻找forwardInvocation,
如果没有找到forwardInvocation,直接unrecognized selector
如果找到forwardInvocation,消息转发。

相关文章

网友评论

    本文标题:iOS底层-动态方法决议与消息转发

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