将 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 运行时库以及其他相关资源。