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