返回

OC底层原理探索之汇编objc_msgSend原理分析

IOS

揭秘 Objective-C 中的神秘黑盒:objc_msgSend 汇编实现

探索 ObjC 消息传递的底层机制

Objective-C 作为一门面向对象的编程语言,其消息传递机制一直是其核心的秘密武器。今天,我们将潜入汇编指令的世界,一步步剖析 objc_msgSend 函数的实现,揭开其神秘的面纱,让你对 ObjC 底层机制有更深入的理解。

汇编指令的逐行分解

objc_msgSend 的汇编实现是 libobjc.a 库中的精妙杰作。对于 arm64 架构,它的代码如下:

_objc_msgSend:
    stp x0, x1, [sp, -16]!     // 保存参数和lr寄存器
    ldr x2, [x0, #8]            // x2 = self
    ldr x0, [x0]                // x0 = cls
    ldp x0, x1, [x0, x2, lsl #3] // x0 = method pointer, x1 = encoding

    ldr x16, [x1, #4]           // x16 = arguments size (encoding low 4 bits)
    add x0, x0, x16, lsl #3     // x0 = actual method pointer
    ldp x2, x3, [sp, #16]        // x2 = receiver, x3 = selector

    br x0                       // jmp to actual method

1. 保存参数和寄存器

旅程的第一步是妥善保存我们的参数和 lr 寄存器,因为我们即将踏入一个未知的领域。stp x0, x1, [sp, -16]! 指令就像一位管家,将这些珍贵的物品整齐地放入栈中,为我们的冒险做好准备。

2. 获取 self 和 cls

现在,我们准备好探索消息传递的对象了。ldr x2, [x0, #8] 指令提取第一个参数(self),而 ldr x0, [x0] 指令获取第二个参数(cls)。

3. 计算方法指针和编码

接下来,是时候计算出方法指针,这是指向实际方法的指南针。ldp x0, x1, [x0, x2, lsl #3] 指令根据 cls 和 self 计算方法指针(x0)和编码(x1)。

4. 计算参数大小

编码中隐藏着一条关键的信息——参数的大小,它存储在低 4 位中。ldr x16, [x1, #4] 指令小心地提取这个值,因为它将帮助我们确定实际方法指针。

5. 计算实际方法指针

万众期待的时刻到了!add x0, x0, x16, lsl #3 指令将参数大小左移 3 位并加到方法指针上,得到我们一直在寻找的实际方法指针(x0)。

6. 恢复 self 和 selector

最后,我们需要恢复 self 和 selector,以便可以将其传递给实际方法。ldp x2, x3, [sp, #16] 指令从栈中检索这些值,为最后的步骤做好准备。

7. 跳转到实际方法

一切就绪,br x0 指令将我们送往实际方法的入口。方法指针就像一扇门,它将我们带到代码中方法的具体位置,让消息可以被处理。

深入理解消息传递机制

通过剖析 objc_msgSend 的汇编实现,我们揭开了 ObjC 消息传递机制的神秘面纱。它是一系列复杂但高效的步骤,协调对象、方法和参数之间的交互。

结论

深入理解 objc_msgSend 的汇编实现是通往 ObjC 底层奥秘的窗口。它不仅使我们能够了解消息传递机制的内部运作,还为深入探索 ObjC 的高级特性奠定了基础。

常见问题解答

  • objc_msgSend 在哪里实现?
    objc_msgSend 的汇编实现位于 libobjc.a 库中。
  • 如何计算方法指针?
    方法指针根据 cls 和 self 通过 ldp x0, x1, [x0, x2, lsl #3] 指令计算。
  • 参数大小在哪里存储?
    参数大小存储在编码的低 4 位中,通过 ldr x16, [x1, #4] 指令提取。
  • 如何跳转到实际方法?
    实际方法指针存储在 x0 寄存器中,br x0 指令用于跳转。
  • objc_msgSend 对 ObjC 语言有什么重要性?
    objc_msgSend 是 ObjC 消息传递机制的核心,它使对象能够与方法和参数进行交互。