objc消息转发机制探索

回顾一下二分查找

sel在method_排序是递增排列的,eg:01,02,03,04,05,06,07,08。markdown

动态方法决议

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);
}
复制代码

代码举例:app

建立一个分类重写resolveInstanceMethod方法oop

+ (BOOL)resolveInstanceMethod:(SEL)sel{

    NSLog(@"%@ 来了",NSStringFromSelector(sel));
    if (sel == @selector(say666)) {
        NSLog(@"%@ 来了",NSStringFromSelector(sel));

        IMP imp           = class_getMethodImplementation(self, @selector(sayMaster));
        Method sayMMethod = class_getInstanceMethod(self, @selector(sayMaster));
        const char *type  = method_getTypeEncoding(sayMMethod);
        return class_addMethod(self, sel, imp, type);
    }
    else if (sel == @selector(sayNB)) {

        IMP imp           = class_getMethodImplementation(objc_getMetaClass("CFPerson"), @selector(cfClassMethod));
        Method sayMMethod = class_getInstanceMethod(objc_getMetaClass("CFPerson"), @selector(cfClassMethod));
        const char *type  = method_getTypeEncoding(sayMMethod);
        return class_addMethod(objc_getMetaClass("CFPerson"), sel, imp, type);
    }
    return NO;
}
复制代码

logMessageSend内部实现

bool objcMsgLogEnabled = false;
static int objcMsgLogFD = -1;

bool logMessageSend(bool isClassMethod,
                    const char *objectsClass,
                    const char *implementingClass,
                    SEL selector)
{
    char	buf[ 1024 ];

    // Create/open the log file
    if (objcMsgLogFD == (-1))
    {
        snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
        objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());
        if (objcMsgLogFD < 0) {
            // no log file - disable logging
            objcMsgLogEnabled = false;
            objcMsgLogFD = -1;
            return true;
        }
    }

    // Make the log entry
    snprintf(buf, sizeof(buf), "%c %s %s %s\n",
            isClassMethod ? '+' : '-',
            objectsClass,
            implementingClass,
            sel_getName(selector));

    objcMsgLogLock.lock();
    write (objcMsgLogFD, buf, strlen(buf));
    objcMsgLogLock.unlock();

    // Tell caller to not cache the method
    return false;
}
复制代码

/temp/msgSends:运行时写入的目录路径ui

instrumentObjcMessageSends 

该方法是监控objc底层消息发送spa

void instrumentObjcMessageSends(BOOL flag)
{
    bool enable = flag;

    // Shortcut NOP
    if (objcMsgLogEnabled == enable) //objcMsgLogEnabled:控制开关
        return;

    // If enabling, flush all method caches so we get some traces
    if (enable)
        _objc_flush_caches(Nil);

    // Sync our log file
    if (objcMsgLogFD != -1)
        fsync (objcMsgLogFD);

    objcMsgLogEnabled = enable;
}
复制代码

建立一个CFPerson类,并添加一个方法sayHellocode

直接运行会在调用方法处崩溃orm

此刻咱们尝试一下用instrumentObjcMessageSends方法将其包裹一下对象

在.m文件里添加实现方法排序

- (id)forwardingTargetForSelector:(SEL)aSelector{
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));

    // runtime + aSelector + addMethod + imp
    return [super forwardingTargetForSelector:aSelector];
}
复制代码

forwardingTargetForSelector来自苹果官方解释:文档

当一个对象发送一个消息没有接收者的时候,则返回其第一接收者。

此时再建立一个CFStudnet接收类供接收

- (id)forwardingTargetForSelector:(SEL)aSelector{
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));

    return [CFStudnet alloc];}
复制代码

对没有进行转发处理的消息将会还调用methodSignatureForSelector方法,具体解释见苹果官方文档

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
    return [NSMethodSignature signatureWithObjCTypes:@"v@:"];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"%s - %@",__func__,anInvocation);
    // GM  sayHello - anInvocation - "抛出去" - anInvocation
    anInvocation.target = [CFStudnet alloc];
    // anInvocation 保存 - 方法
    [anInvocation invoke];
}
复制代码

小结: objc转发流程

相关文章
相关标签/搜索