OC objc_msgSend 汇编语言逻辑与实现

159 阅读4分钟

objc_msgSend 的汇编代码放在 .s 文件中,并使用 .globl 导出该符号 .globl _objc_msgSend

以下是objc_msgSend汇编代码细节和错误处理的完整方法实现:

// 定义全局符号 _objc_msgSend
.globl _objc_msgSend

// 定义 _objc_msgSend 符号的汇编代码
_objc_msgSend:
    // 保存 Callee-Saved 寄存器
    // r4、r5、r6、r7、lr 寄存器在函数调用期间需要保持不变
    push    {r4, r5, r6, r7, lr}

    // 获取当前方法的参数
    // r0: self
    // r1: _cmd
    // r2: ...
    
    // 通过对象的 isa 指针获取类对象并保存到 r4
    ldr     r4, [r0]
    
    // 判断对象是否为 tagged pointer,若是则跳转到 tagged_pointer 部分
    tst     r4, #1
    beq     tagged_pointer
    
    // 对象不是 tagged pointer,继续执行下面的代码
    
    // 通过类对象找到方法的实现
    // 在当前类及其父类中查找方法的实现,保存到 r5
    ldr     r5, [r4, #CLASS_DATA_OFFSET]
    ldr     r5, [r5, #DATA_METHODLIST_OFFSET]
    ldr     r5, [r5]
    
    // 如果找不到方法的实现,则通过消息转发机制处理
    cmp     r5, #0
    beq     forward_invocation
    
    // 获取方法的实现函数指针,保存到 r6
    ldr     r6, [r5, #METHOD_IMPL_OFFSET]
    
    // 跳转到方法的实现
    blx     r6
    
    // 恢复 Callee-Saved 寄存器
    pop     {r4, r5, r6, r7, pc}
    
tagged_pointer:
    // 处理 tagged pointer 的逻辑
    // ...
    // 根据 tagged pointer 的类型进行处理
    
    // 获取 tagged pointer 的值
    ldr     r5, [r4, #TAGGED_POINTER_PAYLOAD_OFFSET]
    
    // 根据 tagged pointer 的类型进行处理
    tst     r4, #OBJC_TAG_MASK
    
    // 处理整数类型的 tagged pointer
    cmp     r4, #OBJC_TAG_MASK_INTEGER
    
    beq     tagged_integer
    
    // 处理浮点数类型的 tagged pointer
    cmp     r4, #OBJC_TAG_MASK_FLOAT
    
    beq     tagged_float
    
    // 处理其他类型的 tagged pointer
    // ...
    
    // 返回结果
    // ...
    
tagged_integer:
    // 处理整数类型的 tagged pointer
    
    // 将 tagged pointer 的值右移 1 bit,获取实际的整数值
    mov     r4, r5, LSR #1
    
    // 返回结果
    // ...
    
tagged_float:
    // 处理浮点数类型的 tagged pointer
    
    // 将 tagged pointer 的值右移 2 bit,获取实际的浮点数值
    mov     r4, r5, LSR #2
    
    // 返回结果
    // ...
    
forward_invocation:
    // 处理消息转发的逻辑
    
    // 获取消息转发的方法签名
    ldr     r5, =@selector(forwardInvocation:)
    ldr     r5, [r5]
    
    // 创建一个 NSInvocation 对象,用于调用 forwardInvocation 方法
    ldr     r6, =NSInvocation
    ldr     r6, [r6]
    ldr     r6, [r6, #OBJC_CLASS_OFFSET]
    
    // 调用 methodSignatureForSelector: 方法获取方法签名
    mov     r7, r0    // self
    mov     r0, r7    // self
    ldr     r1, [r5]  // @selector(methodSignatureForSelector:)
    blx     r1        // 调用 methodSignatureForSelector:
    
    // 检查方法签名是否为 nil,即判断是否能够找到方法
    cmp     r0, #0
    beq     no_method_found
    
    // 调用 invocationWithMethodSignature: 方法创建 NSInvocation 对象
    mov     r0, r6    // self
    ldr     r1, [r6, #OBJC_CLASS_METHODOFFSET]
    blx     r1        // 调用 invocationWithMethodSignature:
    
    // 设置 NSInvocation 对象的 target 和 selector
    mov     r1, r7    // self
    mov     r2, r5    // @selector(forwardInvocation:)
    ldr     r3, [r6, #OBJC_CLASS_METHODOFFSET]
    blx     r3        // 调用 setTarget:
    
    mov     r2, r5    // @selector(forwardInvocation:)
    ldr     r3, [r6, #OBJC_CLASS_METHODOFFSET]
    blx     r3        // 调用 setSelector:
    
    // 将当前方法的参数传递给 forwardInvocation:
    // 将 r2-rn 的参数传递给 NSInvocation 对象
    ldr     r4, [r0, #OBJC_INVOKE_OFFSET]
    
    // 获取当前方法的参数个数
    ldr     r5, [r4, #METHOD_NUM_ARGS_OFFSET]
    
    // 保存 Callee-Saved 寄存器
    push    {r4, r5, r6, r7, lr}
    
    // 处理参数
    sub     sp, sp, r5, LSL #2 // 分配栈空间保存参数
    mov     r6, #2            // 参数从 r2 开始
    add     r7, sp, #0        // r7 指向栈空间
    
    // 保存当前方法的参数到栈中
    mov     r4, r5
arg_loop:
    str     r6, [r7], #4
    add     r6, r6, #1
    subs    r4, r4, #1
    bne     arg_loop
    
    // 调用 invokeWithTarget: 方法
    mov     r0, r6    // self
    ldr     r1, [r6]  // @selector(invokeWithTarget:)
    blx     r1        // 调用 invokeWithTarget:
    
    // 恢复 Callee-Saved 寄存器
    pop     {r4, r5, r6, r7, lr}
    
    // 返回方法调用的结果
    b       return_result

no_method_found:
    // 没有找到方法,抛出异常并导致 CRASH
    ldr     r0, =@selector(doesNotRecognizeSelector:)
    ldr     r0, [r0]
    blx     _objc_msgForward

return_result:
    // 返回方法调用的结果
    // ...

在上述代码中,我们添加了一个 no_method_found 标签来处理没有找到方法的情况。在该标签下,我们调用了 _objc_msgForward 方法,它是一个特殊的函数,负责在没有找到方法时触发消息转发流程或抛出异常。请注意,上述代码仅作为示例,实际的实现可能会有所不同,具体取决于不同的运行时版本和编译器优化。

希望以上内容对您理解 objc_msgSend 方法的细节和错误处理有所帮助。请注意,Objective-C 运行时的实现可能会有细微的差异,具体取决于不同的运行时版本和编译器优化。如需深入了解 Objective-C 运行时的实现细节,建议参考相关的苹果官方文档、开源的 Objective-C 运行时库以及其他相关资源。