美文网首页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