runtime & 消息发送流程 (重磅)
类的走位链
对象 的 isa 指向 类(也可称为类对象)类 的 isa 指向 元类元类 的 isa 指向 根元类,即NSObject根元类 的 isa 指向 它自己起首拓展一下类的结构
objc_class、objc_object、isa、object、NSObject等的团体的关系,如下图所示
bits 的结构
class_ro_t 与 class_rw_t 的关系
通过以上class_rw_t注解中提到的文章,我们对两者有个大要的概念,两者都界说了方法列表,协议列表,属性列表等, 我们来看一下为什么要这么做,这么做有什么意义.
struct class_rw_t { // 重点 // Be warned that Symbolication knows the layout of this structure. uint32_t flags; uint32_t version; const class_ro_t *ro; // 重点 method_array_t methods; property_array_t properties; protocol_array_t protocols; Class firstSubclass; Class nextSiblingClass; char *demangledName;#if SUPPORT_INDEXED_ISA uint32_t index;#endif void setFlags(uint32_t set) { OSAtomicOr32Barrier(set, &flags); } void clearFlags(uint32_t clear) { OSAtomicXor32Barrier(clear, &flags); } // set and clear must not overlap void changeFlags(uint32_t set, uint32_t clear) { assert((set & clear) == 0); uint32_t oldf, newf; do { oldf = flags; newf = (oldf | set) & ~clear; } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags)); }};
- class_rw_t 中包罗 class_ro_t 而且为 const 范例(不可被修改)
class_ro_t 结构
struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize;#ifdef __LP64__ uint32_t reserved;#endif const uint8_t * ivarLayout; const char * name; method_list_t * baseMethodList; protocol_list_t * baseProtocols; const ivar_list_t * ivars; const uint8_t * weakIvarLayout; property_list_t *baseProperties; // This field exists only when RO_HAS_SWIFT_INITIALIZER is set. _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0]; _objc_swiftMetadataInitializer swiftMetadataInitializer() const { if (flags & RO_HAS_SWIFT_INITIALIZER) { return _swiftMetadataInitializer_NEVER_USE[0]; } else { return nil; } } method_list_t *baseMethods() const { return baseMethodList; } class_ro_t *duplicate() const { if (flags & RO_HAS_SWIFT_INITIALIZER) { size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]); class_ro_t *ro = (class_ro_t *)memdup(this, size); ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0]; return ro; } else { size_t size = sizeof(*this); class_ro_t *ro = (class_ro_t *)memdup(this, size); return ro; } }};对比class_rw_t我们发现, class_ro_t中多了 const uint8_t * ivarLayout; const char * name; const ivar_list_t * ivars; const uint8_t * weakIvarLayout;
- objc_class初始化的过程中有一个realizeClassWithoutSwift方法代码体现除了他们的关系, 源码如下
static Class realizeClassWithoutSwift(Class cls){ runtimeLock.assertLocked(); const class_ro_t *ro; class_rw_t *rw; Class supercls; Class metacls; bool isMeta; if (!cls) return nil; if (cls->isRealized()) return cls; assert(cls == remapClass(cls)); // fixme verify class is not in an un-dlopened part of the shared cache? ro = (const class_ro_t *)cls->data(); if (ro->flags & RO_FUTURE) { // This was a future class. rw data is already allocated. rw = cls->data(); ro = cls->data()->ro; cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE); } else { // Normal class. Allocate writeable class data. rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1); rw->ro = ro; rw->flags = RW_REALIZED|RW_REALIZING; cls->setData(rw); } isMeta = ro->flags & RO_META; rw->version = isMeta ? 7 : 0; // old runtime went up to 6 // Choose an index for this class. // Sets cls->instancesRequireRawIsa if indexes no more indexes are available cls->chooseClassArrayIndex(); if (PrintConnecting) { _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s", cls->nameForLogging(), isMeta ? " (meta)" : "", (void*)cls, ro, cls->classArrayIndex(), cls->isSwiftStable() ? "(swift)" : "", cls->isSwiftLegacy() ? "(pre-stable swift)" : ""); } // Realize superclass and metaclass, if they aren't already. // This needs to be done after RW_REALIZED is set above, for root classes. // This needs to be done after class index is chosen, for root metaclasses. // This assumes that none of those classes have Swift contents, // or that Swift's initializers have already been called. // fixme that assumption will be wrong if we add support // for ObjC subclasses of Swift classes. supercls = realizeClassWithoutSwift(remapClass(cls->superclass)); metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));#if SUPPORT_NONPOINTER_ISA // Disable non-pointer isa for some classes and/or platforms. // Set instancesRequireRawIsa. bool instancesRequireRawIsa = cls->instancesRequireRawIsa(); bool rawIsaIsInherited = false; static bool hackedDispatch = false; if (DisableNonpointerIsa) { // Non-pointer isa disabled by environment or app SDK version instancesRequireRawIsa = true; } else if (!hackedDispatch && !(ro->flags & RO_META) && 0 == strcmp(ro->name, "OS_object")) { // hack for libdispatch et al - isa also acts as vtable pointer hackedDispatch = true; instancesRequireRawIsa = true; } else if (supercls && supercls->superclass && supercls->instancesRequireRawIsa()) { // This is also propagated by addSubclass() // but nonpointer isa setup needs it earlier. // Special case: instancesRequireRawIsa does not propagate // from root class to root metaclass instancesRequireRawIsa = true; rawIsaIsInherited = true; } if (instancesRequireRawIsa) { cls->setInstancesRequireRawIsa(rawIsaIsInherited); }// SUPPORT_NONPOINTER_ISA#endif // Update superclass and metaclass in case of remapping cls->superclass = supercls; cls->initClassIsa(metacls); // Reconcile instance variable offsets / layout. // This may reallocate class_ro_t, updating our ro variable. if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro); // Set fastInstanceSize if it wasn't set already. cls->setInstanceSize(ro->instanceSize); // Copy some flags from ro to rw if (ro->flags & RO_HAS_CXX_STRUCTORS) { cls->setHasCxxDtor(); if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) { cls->setHasCxxCtor(); } } // Propagate the associated objects forbidden flag from ro or from // the superclass. if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) || (supercls && supercls->forbidsAssociatedObjects())) { rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS; } // Connect this class to its superclass's subclass lists if (supercls) { addSubclass(supercls, cls); } else { addRootClass(cls); } // Attach categories methodizeClass(cls); return cls;}抽取此中部分
ro = (const class_ro_t *)cls->data(); if (ro->flags & RO_FUTURE) { // This was a future class. rw data is already allocated. rw = cls->data(); ro = cls->data()->ro; cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE); } else { // Normal class. Allocate writeable class data. rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1); rw->ro = ro; rw->flags = RW_REALIZED|RW_REALIZING; cls->setData(rw); }
- 1.bits中一开始存储的class_ro_t
- 2.创建class_rw_t ,而且把class_ro_t赋值给class_rw_t,末了把class_rw_t赋值给bits
- 3.通过源码分析class_ro_t的内容是为const修饰,so 只读
- 4.至于在运行时还可以给class添加方法等操纵,其原理是通过class_rw_t实现的(讲到这里就是前文为什么要做铺垫的原因,为什么可以动态的添加方法和修改方法的imp,这些都和imp的查找流程痛痒干系)
- 5.realizeClass方法中class_rw_t & class_ro_t的创建和赋值动作都是给runtime动态化所做的准备.
那么。。题目来了 rwe 呢??
rwe的统统都来自于extAllocIfNeeded()的创建,
ro属于cleanmemory,在编译即确定的内存空间,只读,可以从内存中移除,必要时再次从文件中加载(多么完满的计划啊)rw属于dirtymemory,rw是运行时结构可读可写,可以向类中添加属性、方法等,在运行时会改变的内存;rwe相当于类的额外信息(动态拓展的时间天生),在现实使用过程中,只有很少的类会真正的改变他们的内容,所rwe的存在是为了制止资源的斲丧
- 1.一旦添加了分类救护创建就一定会有rwe,而且把ro中的methodlist,propertylist,protocollist,classMthods附加到rwe
- 2.addMethods_finish, _class_addProperty,class_addProtocol,objc_duplicateClass,class_setVersion 这些动作都灰天生rwe(以是分类的开销很大)
- 颠末优化后 class_rw_t 只剩下methods、properties、protocols,demangled,而不是添加一个分类又拷贝一份ro数据
进入主题
对象调用方法实在是发送消息的过程,继承者用于定位当前的类,消息主体的sel用于查找对应的方法名,在找到imp
我们在调用一个方法的时间,下层的实现实际上是objc_msgSend(self,_cmd);
- 先找cache_t,再找methodlist
- 步调一 汇编快速查找流程 CacheLookUp(.macro 是一个汇编语言的宏界说)
- 定位到当前的bucket查找方法,假如找到则缓存掷中CacheHit,假如没有找到则往前便遍历bucket,便利到最前面韩式没有找到则移动指向到末了一个继承往前遍历查找,当碰到第一次查找的bucket时,制止快速查找 _msgUncached
- 步调二 C++慢速查找流程 MethodTableLookup _lookUpImpOrForward (慢速查找或转发)
- 1.先查找当前类的metholist(二分),假如查找到 goto Done,而且添加到Cache_t(log_and_fill_cache())->cache_fill()
- 2.假如当前类没有找到,会到父类的Cache中(汇编)快速查找CacheLookUp,假如没有找到继承查找父类的methodlist(二分),假如全部的父类中另有找到,那么imp = forward_imp,实行决议LOOK_RESOLVER
LOOK_RESOLVER 动态方法决议(仅来一次,不会每个月都来一次)
if (slowpath(behavior & LOOK_RESOLVER)){ behavior ^= LOOK_RESOLVER; // 仅走一次 return resolveMethod_locked(inst,sel,cls, behavior);}resolveMethod_locked 它做了什么??
static NEVER_INLINE IMP resolveMethod_locked(id inst,SEL sel,Class cls,int behavior){ rutimeLock.assertLocked(); ASSERT(cls->isRealized()); runtimeLock.unlock(); if (!cls -> isMetaClass()){ resolveInstanceMethod(inst,sel,cls); } else{ resolveInstanceMethod(inst,sel,cls); if (!lookUpImpOrNil(inst,sel,cls)){ resolveInstanceMethod(inst,sel,cls); } } // 再次进入慢速查找 return lookUpImpOrForward(inst,sel,cls,behavior | LOOKUP_CACHE);}static void resolveInstanceMethod(id inst,SEL oldSel,Class cls){ runtimeLock.assertUnlocked(); ASSERT(cls->isRealized()); // 这里的意思是可以在当前类重写resolveInstanceMethod方法(动态添加一次方法的时机) SEL resolve_sel = @selector(resolveInstanceMethod; if (!lookUpImpOrNil(inst,resolve_sel,cls->ISA())){ // Resolver not implemented. return; } BOOL (*msg)(class,SEL,SEL) = (typeof(msg))objc_msgSend; bool resolved = msg(cls,resolve_sel,oldSel); // 假如决议一次之后没有找到,继承lookUpImpOrForward(二分遍历当前类和全部父类),末了实行消息转发 IMP imp = lookUpImpOrNil(inst,oldSel,cls); if (resolved && PrintResolveing){ if (imp){ _objc_inform("RESOLVE: method %c[%s %s]""bynamically resolveed to %p", cls->isMetaClass()?'+':'-', cls->nameForLogging(), sel_getName(oldSel), imp); } else { _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES" ",but no new implementation of %c[%s %s] was found", cls->nameForLogging(),sel_getName(oldSel), cls->isMetaClass()?'+':'-', cls->nameForLogging(),sel_getName(oldSel)); } } }resolveMethod_locked 有什么用??
(可以在当前类重写resolveInstanceMethod方法(动态添加一次方法的时机))
+(BOOL)resolveInstanceMethodSEL)sel{ if (sel == @selector(cxxxx)){ // 添加方法 IMP imp = class_getMethodImplementation(self,@selector(kxxxx)); Method met = class_getInstanceMethod(self,@selector(kxxxx)); const char *type = method_getTypeEncoding(mit); BOOL hasWhy = class_addMethod(self,sel,type); } return [super resolveInstanceMethod:sel];}重点 NSObject中的决议(不发起这么处理惩罚)
#import "NSObject+RC.h"#import <objc/message.h>@implementation NSObject (RC)// 实例方法 动态决议+(BOOL)resolveInstanceMethodSEL)sel{ const char *name = object_getClassName(self); NSString *hasName = [NSString stringWithUTF8String:name]; // 明确面向切面 if (![hasName hasPrefix"RC"])return NO; // 只处理惩罚自己界说的方法 // 对象方法处理惩罚 const char * cx = "cxxxx"; if (sel_getName(sel) == cx){ // 添加方法 IMP imp = class_getMethodImplementation(self,@selector(kxxxx)); Method met = class_getInstanceMethod(self,@selector(kxxxx)); const char *type = method_getTypeEncoding(met); BOOL hasWhy = class_addMethod(self, sel, imp, type); return NO; } // 类方法处理惩罚 const char * sx = "sxxxx"; if (sel_getName(sel) == sx){ // 添加方法 Class currenrClass = objc_getMetaClass("RCPerson"); IMP imp = class_getMethodImplementation(currenrClass,@selector(zxxxx)); Method met = class_getInstanceMethod(currenrClass,@selector(zxxxx)); const char *type = method_getTypeEncoding(met); BOOL hasWhy = class_addMethod(currenrClass, sel, imp, type); return NO; } return NO;}假如没有使用resolveInstanceMethod动态决议呢??(此时imp便是forawrd_imp)
- 当imp == forward_imp 就会走消息转发流程
来到 消息转发流程
forward_imp 的界说:const forward_imp = (IMP)_objc_msgForward_impcache;
_objc_msgForward_impcache (没有开源,以是只能全局搜刮看汇编)找到STATIC_ENTRY __objc_msg_Forward_impcache
STATIC_ENTRY __objc_msg_Forward_impcacheb __objc_msgForwardEND_ENTRY __objc_msg_Forward_impcacheENTRY __objc_msgForwardadrp x17, __objc_forward_handler@PAGEldr p17, [x17, __objc_forward_handler@PAGEOFF]TailCallFunctionPointer x17ENTRY __objc_msgForward汇编形貌 :重要实行在p17证实是指向x17,即搜刮 _objc_forward_handler
- 默认的消息转发处理惩罚objc_defaultForwardHandler
查找结果:void * _objc_forward_handler = (void*)objc_defaultForwardHandler;
objc_defaultForwardHandler 是一个Cxx函数 (这是我们常常见到而且无比认识的)
__attribute__((noreturn,cold))void objc_defaultForwardHandler(id self,SEL sel){ _objc_fatal("%c[%s %s]:unrecognized selector sent to instance %p" "(no message forward handler is installed)", class_isMetaClass(object_getClass(self)) ? '+' : '-', object_getClassName(self), sel_getName(sel), self);}
引入 instrumentObjcMessageSnds 打印消息发送流程(添加到你想要的位置)
extern void instrumentObjcMessageSnds(BOOL flag);// 这种写法是cxx,mian.m 改成 main.mmstruct RCTest{ void *met; RCTest(){ instrumentObjcMessageSnds(YES); } ~RCTest(){ instrumentObjcMessageSnds(NO); }};int main(int argc, char * argv[]) { NSString * appDelegateClassName; @autoreleasepool { RCTest(); NSLog(@"main 函数\n"); appDelegateClassName = NSStringFromClass([AppDelegate class]); } return UIApplicationMain(argc, argv, nil, appDelegateClassName);}消息机制发起在这里处理惩罚
- 1.快速转发 forwardingTargetForSelector:
#import "RPerson.h"#import "RCxxxxx.h"#import <objc/message.h>@implementation RPerson-(id)forwardingTargetForSelectorSEL)aSelector{ const char *sayBye = "sayBye"; if (sel_getName(aSelector) == sayBye){ // 背锅侠 return [RCxxxxx alloc]; // 也可以在这里动态添加方法xxxxx } return [super forwardingTargetForSelector:aSelector];}@end
- 2.慢速转发 methodSignatureForSelector:(须知慢速转发是在快速转发没有相应的环境下才会实行两走其一)
- (NSMethodSignature *)methodSignatureForSelectorSEL)aSelector{ // 1. NSMethodSignature *ms = [NSMethodSignature methodSignatureForSelectorselector(hookHit)]; return ms;}留意:在methodSignatureForSelector 和 forwardInvocation方法之间另有一次动态方法决议,即苹果再次给的一个时机- (void)forwardInvocationNSInvocation *)anInvocation{ // 继承找背锅侠 anInvocation.target = (id)[[RCxxxxx alloc] init]; [anInvocation invoke];}- (void)hookHit{ }总结1 消息流程 CacheLookUp --> _lookUpImpOrForward(而且cache_fill()) --> LOOK_RESOLVER --> _lookUpImpOrForward --> 消息转发流程(imp = forward_imp)-->快速转发(forwardingTargetForSelector) 或 慢速转发(methodSignatureForSelector & forwardInvocation)
- 留意:在methodSignatureForSelector 和 forwardInvocation方法之间另有一次动态方法决议LOOK_RESOLVER,即苹果再次给的一个时机
总结2 实践中重要使用到 消息的处理惩罚机制即 动态方法决议重写 和 消息转发
技能实践:没有开元的文件,就使反汇编(必要获取当地库文件->断住断点(image list)),然后找到一个黑不溜秋的东西,拖入反汇编的APP。
- 前去路径快捷键 command + shift + G
这边文章把握透彻将披荆棘,玉人无数我只取一瓢。。。
|