Rntime
cache_t 篇2:
Runtime消息机制
【消息发送】
Runtime消息机制 当中的 第一步:【消息发送】 流程总结
(1) 当一个对象接收到消息时[obj message];,首先根据obj的isa指针进入它的类
对象cls里面。
我想方法 查找之前,会先检查参数是否合法
是否nil
是否tagged pointer
(2) 在obj的cls里面,首先到缓存cache_t里面查询方法message的函数实现,如
果找到,就直接调用该函数。
(3) 如果上一步没有找到对应函数,在对该cls的方法列表进行二分/遍历查找,如
果找到了对应函数,首先会将该方法缓存到obj的类对象cls的cache_t里面,然后
对函数进行调用。
(4) 在每次进行缓存操作之前,首先需要检查缓存容量,如果缓存内的方法数量
超过规定的临界值(设定容量的3/4),需要先对缓存进行2倍扩容,原先缓存过的
方法全部丢弃,然后将当前方法存入扩容后的新缓存内。
(5) 如果在obj的cls对象里面,发现缓存和方法列表都找不到mssage方法,则通
过cls的superclass指针进入它的父类对象f_cls里面
(6) 进入f_cls后,首先在它的cache_t里面查找mssage,如果找到了该方法,那
么会首先将方法缓存到消息接受者obj的类对象cls的cache_t里面,然后调用方法
对应的函数。
(7) 如果上一步没有找到方法,将会对f_cls的方法列表进行遍历二分/遍历查找,
如果找到了mssage方法,那么同样,会首先将方法缓存到消息接受者obj的类对
象cls的cache_t里面,然后调用方法对应的函数。需要注意的是,这里并不会将
方法缓存到当前父类对象f_cls的cache_t里面。
(8) 如果还没找到方法,则会通过f_cls的superclass进入更上层的父类对象里
面,按照(6)->(7)->(8)步骤流程重复。如果此时已经到了基类对象NSObject,
仍没有找到mssage,则进入步骤(9)
以上找不到就到
未命中:__objc_msgSend_uncached
调用了MethodTableLookup
最终是调用了lookUpImpOrForward函数
(9) 接下来将会转到消息机制的 【动态方法解析】 阶段
Runtime消息机制
2:【动态方法解析】
概念
[obj message] 底层是
objc_msgSend(obj, sel_registerName("message"));
objc 底层一些 blick,msg,sel,等都是.s 文件,也就是汇编代码实现
一些调用频率太高的函数或操作,使用汇编来实现能够提高效率
下面是动态方法 那块代码
类方法类似,不再重复
//--> ️ ️ ️如果到基类还没有找到方法,就尝试进行方法解析
if (resolver && !triedResolver) { // ️ 已经执行过的跳过
runtimeLock.unlockRead();
_class_resolveMethod(cls, sel, inst); // ️ ️ 动态方法解析
runtimeLock.read();
triedResolver = YES; //--> ️ ️ ️ 已经进行过动态方法解析,标记YES
后,后面不会走此步骤
goto retry; //--> ️ ️ ️ 第二步 动态方法解析 执行完,会继续回到 第一
步 消息发送流程
}
重点
通过triedResolver 标记已经执行过,则不再执行,走到 最后消息转发步骤
动态添加的方法已经添加到方法列表里了,不需要再执行
没有执行过,则执行
_class_resolveMethod(cls, sel, inst); 方法,
或者类方法 +(BOOL)resolveClassMethod:(SEL)sel
然后回到 方法列表和缓存查找 初始位置
Runtime流程图
[obj message]
第一步:消息发送
方法列表&cache
缓存表有?
找不着
第二步:动态方法解析
是否执行过第二步?
执行过
第三步:消息转发
已处理?
返回IMP
找不着方法,或者 未处理kill
没有执行过
执行第二步
goto : 第一步 开头位置继续执行
找到
结束
第三步:消息转发
Runtime消息机制
3:【消息转发】
如果 第一步 的方法列表及缓存找不到,同时,第二步 的 triedResolver = YES;
那么 继续下一步骤,就是消息转发
直接上代码,
前面已经贴过
//------> ️ ️ ️ 【Runtime 第三步】:如果方法解析不成功,就进行消息
转发
// Use forwarding.
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlockRead();
return imp;
底层其实调用了CF框架的_CF_forwarding_prep_0,然后就调用了
___forwarding___
int __forwarding__(void *frameStackPointer, int isStret) {
id receiver = *(id *)frameStackPointer;
SEL sel = *(SEL *)(frameStackPointer + 8);
const char *selName = sel_getName(sel);
Class receiverClass = object_getClass(receiver);
// --> ️ ️ ️ 调用 forwardingTargetForSelector:
if (class_respondsToSelector(receiverClass,
@selector(forwardingTargetForSelector:))) {
id forwardingTarget = [receiver forwardingTargetForSelector:sel];
if (forwardingTarget && forwardingTarget != receiver) {
return objc_msgSend(forwardingTarget, sel, ...);
}
}
// --> ️ ️ ️ 调用 methodSignatureForSelector 获取方法签名后再调用
forwardInvocation
if (class_respondsToSelector(receiverClass,
@selector(methodSignatureForSelector:))) {
NSMethodSignature *methodSignature = [receiver
methodSignatureForSelector:sel];
if (methodSignature && class_respondsToSelector(receiverClass,
@selector(forwardInvocation:))) {
NSInvocation *invocation = [NSInvocation
_invocationWithMethodSignature:methodSignature
frame:frameStackPointer];
[receiver forwardInvocation:invocation];
void *returnValue = NULL;
[invocation getReturnValue:&value];
return returnValue;
}
}
// --> ️ ️ ️ 调用找不到方法 does Not Recognize Selector ,如果没实
现就kill 了
if
(class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSel
ector:))) {
[receiver doesNotRecognizeSelector:sel];
}
// The point of no return.
kill(getpid(), 9);
}
上述代码__forwarding__函数逻辑可以简单概括成
forwardingTargetForSelector:
->methodSignatureForSelector
->forwardInvocation
->doesNotRecognizeSelector:sel -> kill
消息转发详细步骤
1. -(id)forwardingTargetForSelector:(SEL)aSelector
返回一个对象,或者返回nil
目的:交给其它对象处理
nil:执行下一步
非nil,让这个对象处理
2.-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 创
建方法签名
目的:创建签名MethodSignature ,
为第三步做准备
有签名
无签名
to第3步
_invocationWithMethodSignature
方法获取 NSInvocation
2.1.- (void)forwardInvocation:(NSInvocation *)anInvocation
使用anInvocation ,或者自定义配置anInvocation 对象 。
1. anInvocation.target -- 方法调用者
2. anInvocation.selector -- 方法名
执行消息:[anInvocation invoke];
3. doesNotRecognizeSelector: 是否实现这个方法
实现了,直接调用
未实现,走kill ,结束程序
tag