NSMethodSignature 详解

1,537 阅读2分钟

本文将利用holper disassembler class dump 窥探KVO背后的原理。

CoreFoundation.framework

关于如何提取 CoreFoundation.framework,请参考这里

NSMethodSignature

NSInvocation时,需要方法签名,而NSMethodSignature构造方法如下, 在反编译后,这两个方法其内部实现其实是一致的,如下图



- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
//___methodDescriptionForSelector([rdi class], rdx) != 0x0

+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
//___methodDescriptionForSelector(rdi, rdx) != 0x0

同样的方法,在 ___methodDescriptionForSelector 都是传入的类对象

___methodDescriptionForSelector

长代码警告


int ___methodDescriptionForSelector(int arg0, int arg1) {
    
    // arg0:类对象
    // arg1:SEL
    r15 = arg1;  //SEL
    var_38 = arg0;  //类对象
    //非空 去 loc_141cf1
    if (arg0 == 0x0) goto loc_141def;

loc_141cf1:
    r12 = var_38;  //类对象
    var_48 = r15;  //SEL
    goto loc_141d00;

loc_141d00:
    // protocolList
    // 这里声明了一个空指针传入0x0,
    // 如果class_copyProtocolList 有值 会赋在该指针上,类似protertyList用法
    rbx = class_copyProtocolList(r12, 0x0); //方法返回:Protocol
    // 如果没有protocolList 否则去loc_141d20
    if (0x0 == 0x0) goto loc_141da0;

loc_141d20:
    // 为上面的 class_copyProtocolList count指针
    r13 = 0x0; 
    //protocol 对象
    var_40 = rbx; 
    goto loc_141d30;

loc_141d30:

    //遍历 protocol
    r14 = *(rbx + r13 * 0x8);
    rax = class_isMetaClass(r12);
    rax = protocol_getMethodDescription(r14, r15, 0x1, (rax ^ 0x1) & 0xff);
    rdi = r12;
    if (rax != 0x0) goto loc_141db0;

loc_141d59:
    r14 = *(rbx + r13 * 0x8);
    rbx = rdi;
    rax = class_isMetaClass(rdi);
    rax = protocol_getMethodDescription(r14, r15, 0x0, (rax ^ 0x1) & 0xff);
    r14 = 0x0;
    if (rax != 0x0) goto loc_141dc0;

loc_141d82:
    r13 = r13 + 0x1;
    r15 = var_48;  
    r12 = rbx;
    rbx = var_40;
    if (r13 < 0x0) goto loc_141d30;

loc_141d98:
    r15 = 0x0;
    goto loc_141dca;

loc_141dca:
    free(rbx);
    if (r15 != 0x0) goto loc_141e17;

loc_141dd7:
    //获取父类
    rax = class_getSuperclass(r12);
    r12 = rax;    //父类对象
    r15 = var_48; //SEL
    if (rax != 0x0) goto loc_141d00;

loc_141def:
    //如果没有父类,根据 类对象,SEL 获取Method
    rax = class_getInstanceMethod(var_38, r15);
    if (rax != 0x0) {
            //根据Method 获取Description
            rax = method_getDescription(rax);
            r15 = *rax;
            r14 = *(rax + 0x8);
    }
    else {
            r15 = 0x0;
            r14 = 0x0;
    }
    goto loc_141e17;

loc_141e17:
    //返回 Description 可能为nil
    rax = r15;
    return rax;

loc_141dc0:
    r15 = rax;
    r12 = rbx;
    rbx = var_40;
    goto loc_141dca;

loc_141db0:
    r15 = rax;
    r14 = 0x1;
    r12 = rdi;
    goto loc_141dca;

loc_141da0:
    // 如果Protocol 为空 去 loc_141dd7
    if (rbx == 0x0) goto loc_141dd7;

loc_141da5:
    r14 = 0x0;
    r15 = 0x0;
    goto loc_141dca;
}

这里面的代码去其实可读性很清晰,下面我们来梳理下调用流程

img

NSInvocation

对于NSInvocation有个疑问

NSInvocation是以什么方式调用的

经过尝试了各种办法之后,我终于得到了一下信息

img
配合反编译的结果
img
我们可以确定,[NSInvocation invoke] 最后还是走了msgSend的过程