iOS 消息机制2

214 阅读5分钟

「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战」。

上一篇对消息发送,方法查找 了解了,这一篇就来看看当在缓存,类,所有父类里都找不到方法时,进入动态方法解析的流程。
基于objc4-779.1源码
当我们调用一个不存在的方法时\

image.png

可以看出__forwarding_prep_0___ 函数调用了 ___forwarding___函数,接着调用了 doesNotRecognizeSelector方法,最后抛出异常.而且是发生在CoreFoundation。(这些先放一放)

接着上一篇先看看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);
}

当我们调用一个方法,哪都没有找到的时候,这个时候就会调用resolveMethod_locked,尝试动态方法解析。
在方法里加个断点一步步往下看看

if (sel == @selector(tttt)) {
            
    }

通过堆栈信息可以看到流程_objc_msgForward_impcache->_objc_msgForward->_objc_forward_handler->__forwarding_prep_0___->___forwarding___
又是头疼的汇编。

看看_objc_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

流程 确实如此 _objc_msgForward_impcache->_objc_msgForward->_objc_forward_handler
这个handler回调 容易理解
那么这个回调什么时候设置的呢。
源码里面搜索可以发现 默认的_objc_forward_handlerobjc_defaultForwardHandler,它干的活就是打印日志触发 crash。所以要实现消息转发,就得给它赋予新的值,这个时候就要用到objc_setForwardHandler

void *_objc_forward_handler = (void*)objc_defaultForwardHandler;

void objc_setForwardHandler(void *fwd, void *fwd_stret)
{
    _objc_forward_handler = fwd;
#if SUPPORT_STRET
    _objc_forward_stret_handler = fwd_stret;
#endif
}

而这一部分是发生在CoreFoundation中,而其源码里面这些部分却被删掉了,根本看不见。这个时候我们只能通过反编译看看了。

CoreFoundation的可执行文件放到Hopper中。可以看到一个初始化___CFInitialize

我们看看它的伪代码

image.png

果然 可以看到初始化的时候将___forwarding_prep_0______forwarding_prep_1___,作为参数调用了objc_setForwardHandler。(___forwarding_prep_1___是结构体相关的)
so我们来看看___forwarding_prep_0___的实现

image.png

这一下前面我们通过断点抓到的堆栈流程_objc_msgForward_impcache->_objc_msgForward->_objc_forward_handler->__forwarding_prep_0___->___forwarding___ 就通了。
接下来就是___forwarding___了,消息转发的逻辑就在这了

先看它的伪代码。

int ____forwarding___(int arg0, int arg1) {
    var_38 = arg1;
    r15 = arg0;
    rax = COND_BYTE_SET(NE);
    r13 = _objc_msgSend;
    if (arg1 != 0x0) {
            r13 = _objc_msgSend_stret;
    }
    var_30 = *(r15 + (rax & 0xff) * 0x8 + 0x8);
    rbx = *(r15 + (rax & 0xff) * 0x8);
    r14 = (rax & 0xff) * 0x8;
    if ((rbx >= 0x0) || ((rbx & 0x7000000000000000) != 0x0)) goto loc_9cc0d;

loc_9ce24:
    r12 = _getAtomTarget(rbx);
    *(r15 + r14) = r12;
    ___invoking___(r13, r15, r15, 0x400, 0x0);
    if (*r15 == r12) {
            *r15 = rbx;
    }
    goto loc_9ce51;

loc_9ce51:
    rax = r15;
    return rax;

loc_9cc0d:
    var_40 = r14;
    r12 = object_getClass(rbx);
    r14 = class_getName(r12);
    if (class_respondsToSelector(r12, @selector(forwardingTargetForSelector:)) == 0x0) goto loc_9cc80;

loc_9cc3a:
    rax = [rbx forwardingTargetForSelector:var_30];
    if ((rax == 0x0) || (rax == rbx)) goto loc_9cc80;

loc_9cc58:
    r14 = var_40;
    if ((rax >= 0x0) || ((rax & 0x7000000000000000) != 0x0)) goto loc_9cc74;

loc_9ce21:
    rbx = rax;
    goto loc_9ce24;

loc_9cc74:
    *(0x0 + r14) = rax;
    r15 = 0x0;
    goto loc_9ce51;

loc_9cc80:
    var_40 = rbx;
    if (strncmp(r14, "_NSZombie_", 0xa) == 0x0) goto loc_9ce63;

loc_9cca0:
    r14 = var_40;
    r13 = var_38;
    if (class_respondsToSelector(r12, @selector(methodSignatureForSelector:)) == 0x0) goto loc_9ceb4;

loc_9ccbf:
    r12 = [r14 methodSignatureForSelector:var_30];
    if (r12 == 0x0) goto loc_9cf11;

loc_9ccdf:
    rbx = [r12 _frameDescriptor];
    if (((*(int16_t *)(*rbx + 0x22) & 0xffff) >> 0x6 & 0x1) != r13) {
            rdx = sel_getName(var_30);
            rsi = "";
            r8 = " not";
            rcx = r8;
            if ((*(int16_t *)(*rbx + 0x22) & 0xffff & 0x40) != 0x0) {
                    rcx = rsi;
            }
            if (r13 != 0x0) {
                    r8 = rsi;
            }
            _CFLog(0x4, @"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'.  Signature thinks it does%s return a struct, and compiler thinks it does%s.", rdx, rcx, r8, r9, stack[2039]);
    }
    r13 = rbx;
    rbx = [NSInvocation _invocationWithMethodSignature:r12 frame:r15];
    if (class_respondsToSelector(object_getClass(r14), @selector(forwardInvocation:)) != 0x0) {
            [r14 forwardInvocation:rbx];
    }
    else {
            _CFLog(0x4, @"*** NSForwarding: warning: object %p of class '%s' does not implement forwardInvocation: -- dropping message", r14, object_getClassName(r14), r8, r9, stack[2039]);
    }
    if (rbx->_retainedArgs != 0x0) {
            rax = *r13;
            if ((*(int8_t *)(rax + 0x22) & 0x80) != 0x0) {
                    rcx = rbx->_frame;
                    rdx = *(int32_t *)(rax + 0x1c);
                    rsi = *(int8_t *)(rax + 0x20) & 0xff;
                    memmove(*(r15 + rsi + rdx), *(rcx + rsi + rdx), *(int32_t *)(*rax + 0x10));
            }
    }
    r15 = rbx->_retdata;
    if ((*(int8_t *)[r12 methodReturnType] & 0xff) == 0x44) {
            asm{ fld        tword [r15] };
    }
    goto loc_9ce51;

loc_9cf11:
    r15 = sel_getName(var_30);
    r8 = sel_getUid(r15);
    r12 = var_30;
    if (r8 != var_30) {
            _CFLog(0x4, @"*** NSForwarding: warning: selector (%p) for message '%s' does not match selector known to Objective C runtime (%p)-- abort", r12, r15, r8, r9, stack[2039]);
    }
    if (class_respondsToSelector(object_getClass(r14), @selector(doesNotRecognizeSelector:)) != 0x0) {
            rax = [r14 doesNotRecognizeSelector:r12];
            asm{ int3 };
    }
    else {
            rax = _CFLog(0x4, @"*** NSForwarding: warning: object %p of class '%s' does not implement doesNotRecognizeSelector: -- abort", r14, object_getClassName(r14), r8, r9, stack[2039]);
            asm{ int3 };
    }
    return rax;

loc_9ceb4:
    rbx = class_getSuperclass(r12);
    r15 = object_getClassName(r14);
    if (rbx == 0x0) {
            _CFLog(0x4, @"*** NSForwarding: warning: object %p of class '%s' does not implement methodSignatureForSelector: -- did you forget to declare the superclass of '%s'?", r14, r15, object_getClassName(r14), r9, stack[2039]);
    }
    else {
            _CFLog(0x4, @"*** NSForwarding: warning: object %p of class '%s' does not implement methodSignatureForSelector: -- trouble ahead", r14, r15, r8, r9, stack[2039]);
    }
    goto loc_9cf11;

loc_9ce63:
    if (*(int8_t *)___CFOASafe != 0x0) {
            ___CFRecordAllocationEvent();
    }
    rax = _CFLog(0x3, @"*** -[%s %s]: message sent to deallocated instance %p", r14 + 0xa, sel_getName(var_30), var_40, r9, stack[2039]);
    asm{ int3 };
    return rax;
}

虽然不懂汇编,但是结合伪代码,大致也能看看。

首先是查看是否实现forwardingTargetForSelector方法,没有就去loc_9cc80

image.png

这里有个僵尸对象的判断,我们往下,查看是否实现methodSignatureForSelector,

image.png

  • 如果没有响应,跳转至loc_9ceb4,则直接报错

  • 如果获取methodSignatureForSelector是nil,也直接报错\

image.png

image.png

image.png

如果methodSignatureForSelector不为空,则调用 forwardInvocation执行 NSInvocation

image.png

至此 动态方法解析流程 分析完毕,有误请大佬指正。