Runtime - 消息发送机制
objc_msgSend执行流程
- OC中的方法调用,其实都是转换为objc_msgSend函数的调用
- objc_msgSend的执行流程可以分为3大阶段
- 消息发送
- 动态方法解析
- 消息转发
源码跟读
objc-msg-arm64.s
- ENTRY _objc_msgSend
- b.le LNilOrTagged
- CacheLookup NORMAL
- .macro CacheLookup
- .macro CheckMiss
- STATIC_ENTRY __objc_msgSend_uncached
- .macro MethodTableLookup
- __class_lookupMethodAndLoadCache3
objc-runtime-new.mm
- _class_lookupMethodAndLoadCache3
- lookUpImpOrForward
- getMethodNoSuper_nolock、search_method_list、log_and_fill_cache
- cache_getImp、log_and_fill_cache、getMethodNoSuper_nolock、log_and_fill_cache
- _class_resolveInstanceMethod
- _objc_msgForward_impcache
objc-msg-arm64.s
- STATIC_ENTRY __objc_msgForward_impcache
- ENTRY __objc_msgForward
Core Foundation
- __forwarding__(不开源)
消息发送 - objc_msgSend执行流程1
- receiver是否为nil
- 是,退出
- 否
- 从receiverClass的cache中查找方法
- 找到方法 -> 调用方法结束查找
- 没有找到方法
- 从receiverClass的
class_rw_t中查找方法- 找到方法 -> 调用方法结束查找,并将方法缓存到receiverClass的cache中
- 未找到方法
- 从superClass的
cache中查找方法- 找到方法 -> 调用方法结束查找,并将方法缓存到receiverClass的cache中
- 未找到方法
- 从
superClass的cache中查找方法- 找到方法 -> 调用方法结束查找,并将方法缓存到receiverClass的cache中
- 未找到方法
- 上层是否还有
superClass- 是,继续 从superClass的cache中查找方法
- 否,进入
动态方法解析阶段
- 上层是否还有
- 从
- 从superClass的
- 从receiverClass的
- 从receiverClass的cache中查找方法
- 如果是从class_rw_t中查找方法
- 已经排序的,二分查找
- 没有排序的,遍历查找
- receiver通过isa指针找到receiverClass
- receiverClass通过superClass指针找到superClass
动态方法解析 - objc_msgSend执行流程2
- 是否曾经有动态解析
- 是,进入
消息转发阶段 - 否
- 调用+resolveInstanceMethod:或者+resolveClassMethod:方法来动态解析方法
- 标记为已经动态解析
- 消息发送
- 是,进入
1 | void other(id self, SEL _cmd) { |
开发者可以实现以下方法,来动态添加方法实现
- +resolveInstanceMethod:
- +resolveClassMethod:
动态解析过后,会重新走“消息发送”的流程
“从receiverClass的cache中查找方法”这一步开始执行
消息转发 - objc_msgSend执行流程3
- 调用
forwardingTargetForSelector:方法- 返回值不为nil -> objc_msgSend(返回值, SEL)
- 返回值为nil
- 调用
methodSignatureForSelector:方法- 返回值不为nil 调用
forwardInvocation:方法 - 返回值为nil
- 调用
doesNotRecognizedSelector:方法(常见的方法找不到错误)
- 调用
- 返回值不为nil 调用
- 调用
示例:
1 | - (id)forwardingTargetForSelector:(SEL)aSelector { |
开发者可以在
forwardInvocation:方法中自定义任何逻辑
且:以上方法都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-)
补充:
@dynamic是告诉编译器不用自动生成getter和setter的实现,等到运行时再添加方法实现
Super的本质
- super调用,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数
- struct objc_super2
- SEL
结构体如下:
- receiver是消息接收者
- current_class是recevier的class对象
1 | struct objc_super2 { |
LLVM的中间代码(IR)
- Objective-C在变为机器代码之前,会被LLVM编译器转换为中间代码(Intermediate Representation)
可以使用以下命令行指令生成中间代码
- clang -emit-llvm -S main.m
语法简介
@ - 全局变量
% - 局部变量
alloca - 在当前执行的函数的堆栈帧中分配内存,当该函数返回到其调用者时,将自动释放内存
i32 - 32位4字节的整数
align - 对齐
load - 读出,store 写入
icmp - 两个整数值比较,返回布尔值
br - 选择分支,根据条件来转向label,不根据条件跳转的话类似 goto
label - 代码标签
call - 调用函数
- 具体可以参考官方文档:https://llvm.org/docs/LangRef.html
参考
李明杰老师课件- objc源码
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 ivan's Blog!
评论









