iOS-底层原理10-动态方法决议&消息转发

《iOS底层原理文章汇总》
上一篇文章《iOS-底层原理09-msgSend消息转发》中提到,如果慢速查找在父类的缓存中没有找到,则传入父类的class,进而重新进行父类的慢速查找流程...一层层递归循环,直到找到方法的Imp后返回。实质上并不是走的这个递归循环,因为在父类的缓存中走汇编代码快速查找找不到之后,并不会进入__objc_msgSend_uncached,就不会走入MethodTableLookup iOS-底层原理10-动态方法决议&消息转发
文章图片
CacheLookupGETIMP_cache_getImp@2x.png 从父类的缓存中查找的流程imp = cache_getImp(curClass, sel); 走到汇编,CacheLookup GETIMP, _cache_getImp,CacheLookup将参数GETIMP带入到寄存器p0中,
iOS-底层原理10-动态方法决议&消息转发
文章图片
父类缓存中查找传入GETIMP.png 当父类的缓存中一直没有找到此方法时,则进入JumpMiss/CheckMiss $0,
iOS-底层原理10-动态方法决议&消息转发
文章图片
GETIMP@2x.png 从而判断$0是否等于GETIMP,没有找到就是GETIMP,进入cbz p9, LGetImpMiss,将空值0存入寄存器p0位置,并直接返回ret = nil。并没有进行从汇编开始的慢速查找递归循环。
iOS-底层原理10-动态方法决议&消息转发
文章图片
_cache_getImp@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
从父类的缓存中查找返回ret@2x.png 故以上根本没有再次进入父类的方法慢速查找流程,递归循环,正确的流程图如下 iOS-底层原理10-动态方法决议&消息转发
文章图片
方法的慢速查找流程---正确.png
  • 方法查找流程:先进入本类的快速查找流程->本类的多线程缓存中查找->本类的慢速查找(二分查找)->父类的缓存中查找->父类的慢速查找(二分查找),如果还没找到呢?且不做任何处理。程序将会崩溃报错。
  • 通过代码调试能够看到进入了父类的实例方法慢速查找流程,新建LGMankind为LGPerson的父类,本类实例方法慢速查找流程LGPerson->LGMankind->NSObject->nil
27.gif 类中没有实现对象方法或类方法,调用的对象方法或类方法的时候会报错,最常见的错误,最熟悉的陌生人
unrecognized selector sent to instance 0x10102c040
  • 调用没有实现的对象方法
-[LGPerson say666]: unrecognized selector sent to instance 0x10102c040 2020-11-03 20:25:47.362425+0800 KCObjc[2999:669876] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[LGPerson say666]: unrecognized selector sent to instance 0x10102c040'

iOS-底层原理10-动态方法决议&消息转发
文章图片
h@2x.png
iOS-底层原理10-动态方法决议&消息转发
文章图片
m@2x.png
iOS-底层原理10-动态方法决议&消息转发
文章图片
main@2x.png
iOS-底层原理10-动态方法决议&消息转发
文章图片
unrecognized.jpg
24.gif
  • 调用没有实现的类方法
iOS-底层原理10-动态方法决议&消息转发
文章图片
25.gif iOS-底层原理10-动态方法决议&消息转发
文章图片
类方法未实现.png 没有实现对象方法或类方法,则会返回forward_imp,forward_imp = (IMP)_objc_msgForward_impcache; _objc_msgForward_impcache是汇编实现,汇编到C++多一个下划线_,C++到C少一个下划线_。
// Default forward handler halts the process. __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); } void *_objc_forward_handler = (void*)objc_defaultForwardHandler;

iOS-底层原理10-动态方法决议&消息转发
文章图片
forward_imp.jpg __objc_msgForward_impcache@2x.png _objc_forward_handler@2x.png 26.gif 没有实现实例方法或类方法,系统提供一次挽救的机会实现:动态方法决议 1.实例方法的动态方法决议 根据isa的走位图,实例方法慢速查找流程LGPerson->LGMankind->NSObject->nil
重写类方法+ (BOOL)resolveInstanceMethod:(SEL)sel,给类添加一个sayMaster的方法,在编译类加载的时候,+ (BOOL)resolveInstanceMethod:(SEL)sel就已经加载进内存了,将sayMaster的实现Imp写进sel中。
iOS-底层原理10-动态方法决议&消息转发
文章图片
resolveInstanceMethod.png
+ (BOOL)resolveInstanceMethod:(SEL)sel{if (sel == @selector(say666)) { NSLog(@"%@ 来了哦",NSStringFromSelector(sel)); IMP imp= class_getMethodImplementation(self, @selector(sayMaster)); Method sayMMethod = class_getInstanceMethod(self, @selector(sayMaster)); const char *type= method_getTypeEncoding(sayMMethod); return class_addMethod(self, sel, imp, type); }return [super resolveInstanceMethod:sel]; }

iOS-底层原理10-动态方法决议&消息转发
文章图片
动态方法决议输出.png 若动态方法决议+ (BOOL)resolveInstanceMethod:(SEL)sel里面,没有对sel方法say666进行重新指定imp,则此动态决议方法+ (BOOL)resolveInstanceMethod:(SEL)sel会走两次,为什么会走两次呢???
iOS-底层原理10-动态方法决议&消息转发
文章图片
resloveInstanceMethod两次@2x.png
28.gif
  • 第一次动态方法决议后,方法返回,是什么时候进入第二次动态方法决议方法的呢???通过打印第二进入前的堆栈情况,获取堆栈信息如下,得到第二次触发是在CoreFoundation`-[NSObject(NSObject) methodSignatureForSelector:],后面探索实质为消息的慢速转发流程。
iOS-底层原理10-动态方法决议&消息转发
文章图片
第二次动态方法决议进入堆栈@2x.png
  • 第二次动态方法决议还没找到imp,程序报错_objc_msgForward_impcache
29.gif iOS-底层原理10-动态方法决议&消息转发
文章图片
第二次动态方法决议没找到imp报错@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
第二次动态方法决议.png 2.类方法的动态方法决议 根据isa的走位图,查找类方法流程元类(metaClass)->根元类(rootMetaClass:NSObject)->根类(NSObject)->nil,根元类继承于NSObject。
iOS-底层原理10-动态方法决议&消息转发
文章图片
isa流程图.png iOS-底层原理10-动态方法决议&消息转发
文章图片
isa走位.png
  • 查找类方法,先传入LGPerson的元类的地址0x0000000100002270,进行查找类方法sayNB,获取类方法,类方法在元类中是以实例方法的形式存在的,类中存在两个类方法+ (void)lgClassMethod和+ (BOOL)resolveInstanceMethod:(SEL)sel
iOS-底层原理10-动态方法决议&消息转发
文章图片
类方法@2x.png
  • 1 通过lldb获取LGPerson元类中的方法,相当于获取类方法的个数两个+ (void)lgClassMethod和+ (BOOL)resolveInstanceMethod:(SEL)sel
iOS-底层原理10-动态方法决议&消息转发
文章图片
lldb获取类方法@2x.png
  • 方法列表中不存在sayNB的类方法,此时继续for循环在LGPerson元类的父类,也就是根元类NSObject中查找sayNB方法,curClass = curClass->superclass,curClass的地址为0x00000001003340f0
iOS-底层原理10-动态方法决议&消息转发
文章图片
根元类中查找方法@2x.png
  • 2 通过lldb查看根元类NSObject中的方法个数
iOS-底层原理10-动态方法决议&消息转发
文章图片
根元类NSObject中的方法@2x.png
iOS-底层原理10-动态方法决议&消息转发
文章图片
LGPerson根元类NSObject的方法数量@2x.png
  • 3 根元类中没有找到sayNB方法,继续往上查找,在根元类的父类NSObject中继续查找sayNB方法
iOS-底层原理10-动态方法决议&消息转发
文章图片
LGPerson根元类NSObject中查找LGPerson的类方法@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
NSObject中的方法@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
NSObject的父类中查找sayNB方法@2x.png
  • 4 根元类的父类中没有找到sayNB方法,进入类方法的动态方法决议
  • curClass = curClass->superclass,curClass的地址为0x0000000100334140,没有找到进入动态方法决议return resolveMethod_locked(inst, sel, cls, behavior); 此时cls传入的是LGPerson的元类,会走入resolveClassMethod方法,能通过实现此方法,对方法进行重新添加,先看一看没有实现此方法的情况下,流程接下来往哪里走?
resolveClassMethod(inst, sel, cls); if (!lookUpImpOrNil(inst, sel, cls)) { resolveInstanceMethod(inst, sel, cls); }

  • 没有实现resolveClassMethod:类方法,此时lookUpImpOrNil(inst, @selector(resolveClassMethod:), cls)一层一层的往上找,LGPerson的元类0x0000000100002270中无法找到,继续往上一层LGPerson的根元类(NSObject)0x00000001003340f0中继续查找,LGPerson的根元类是NSObject的元类,存放了NSObject的类方法,正好resolveClassMethod:也是NSObject的类方法,所以下面的return语句永远都不会走。
  • 前面如果没有实现,后面在NSObject的元类中也就是LGPerson的根元类中一定能找到系统NSObject的类方法resolveClassMethod:的实现,下面return永远不会走。
if (!lookUpImpOrNil(inst, @selector(resolveClassMethod:), cls)) { // Resolver not implemented. return; }

iOS-底层原理10-动态方法决议&消息转发
文章图片
NSObject的resolveClassMethod@2x.png 流程继续往下bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel); 返回false,因为resolveClassMethod:方法并没有在LGPerson中实现,sayNB方法更没有添加。往下执行IMP imp = lookUpImpOrNil(inst, sel, cls); 查找流程,仍然找不到sayNB方法。
iOS-底层原理10-动态方法决议&消息转发
文章图片
nonmeta@2x.png
  • 进入resolveInstanceMethod(inst, sel, cls); ,查看根元类cls->ISA()中是否有实例方法resolveInstanceMethod:存在,是有的,根元类是NSObject的元类,NSObject的类方法resolveInstanceMethod:在NSObjct的元类中以实例方法存在,能找到,不会走return。 但bool resolved = msg(cls, resolve_sel, sel); 为false,因为LGPerson的元类中并不存在resolveInstanceMethod:和sayNB。
iOS-底层原理10-动态方法决议&消息转发
文章图片
类方法进入resolveInstanceMethod@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
根元类NSObject中的resolveInstanceMethod方法@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
resolveInstanceMethod@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
类方法sayNB找不到进入resolveInstanceMethod@2x.png 若实现了resolveInstanceMethod:方法,添加了sayNB方法还会报错吗???等到后面分析
  • IMP imp = lookUpImpOrNil(inst, sel, cls); 查找sayNB方法,找不到返回imp为nil,此时,动态方法决议结束,再次查找一遍sayNB方法,确认有没有添加。return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE); ,未找到sayNB,返回(IMP) imp = 0x00000001002c262c (libobjc.A.dylib`_objc_msgForward_impcache),报错unrecognized selector sent to instance 0x10102c040
iOS-底层原理10-动态方法决议&消息转发
文章图片
objc_msgForward_impcache@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
类方法未实现.png 第一次动态方法决议结束
  • 后面的流程往哪里走呢???后面再探索,下面先看下常规操作,类方法sayNB找不到,防止报错的动态方法决议的处理
查询LGPerson的对象方法传入的cls是类LGPerson
iOS-底层原理10-动态方法决议&消息转发
文章图片
LGPerson的对象方法@2x.png
iOS-底层原理10-动态方法决议&消息转发
文章图片
LGPerson的对象方法传入的cls为类LGPerson@2x.png
(lldb) p cls (Class) $11 = LGPerson (lldb) p/x cls (Class) $12 = 0x00000001000032f0 LGPerson

查询LGPerson的类方法传入的cls是元类LGPerson
iOS-底层原理10-动态方法决议&消息转发
文章图片
LGPerson的类方法sayNB@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
LGPerson类方法查询传入cls为元类LGPerson@2x.png 类方法的动态方法决议 1.重写resolveClassMethod:方法
sayNB方法找不到,根据前文分析会进入resolveClassMethod(inst, sel, cls); ,此时的cls为LGPerson的元类LGPerson,进入resolveClassMethod(inst, sel, cls); ,查询LGPerson有没有实现resolveClassMethod:方法,虽然目前LGPerson中没有实现resolveClassMethod:类方法,但LGPerson元类的父类,也就是根元类NSObject中,存在resolveClassMethod:方法,为什么呢?
因为系统类NSObject的类方法resolveClassMethod:存在NSObject的元类中以实例方法形式存在,即根元类NSObject中。
iOS-底层原理10-动态方法决议&消息转发
文章图片
NSObject的resolveClassMethod@2x.png
  • 1.如果啥也不做,必定会报错,最熟悉的陌生人
iOS-底层原理10-动态方法决议&消息转发
文章图片
unrecognizedselector@2x.png
  • 2.动态方法决议重写resolveClassMethod:方法,但不增加sayNB的Imp,动态方法决议中能迅速找到方法resolveClassMethod(inst, sel, cls);
iOS-底层原理10-动态方法决议&消息转发
文章图片
实现resolveClassMethod方法为添加sayNBImp@2x.png 没有添加sayNB的imp,bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel); 仍然为false
iOS-底层原理10-动态方法决议&消息转发
文章图片
resolveClassMethod@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
未添加sayNB的imp,resolved为false@2x.png 添加sayNB的imp之后,看resolved的值bool resolved = msg(nonmeta, @selector(resolveClassMethod:),此时为true
iOS-底层原理10-动态方法决议&消息转发
文章图片
添加sayNB的imp之后resolved为true@2x.png 此时能找到LGPerson的类方法sayNB:,为什么能找到呢???
先看编译时的,sayNB方法添加过程
iOS-底层原理10-动态方法决议&消息转发
文章图片
LGPerson添加sayNB的imp@2x.png
+ (BOOL)resolveClassMethod:(SEL)sel{ NSLog(@"%@ 来了",NSStringFromSelector(sel)); if (sel == @selector(sayNB)) { IMP imp= class_getMethodImplementation(objc_getMetaClass("LGPerson"), @selector(lgClassMethod)); Method sayMMethod = class_getInstanceMethod(objc_getMetaClass("LGPerson"), @selector(lgClassMethod)); const char *type= method_getTypeEncoding(sayMMethod); return class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type); } return [super resolveClassMethod:sel]; }

iOS-底层原理10-动态方法决议&消息转发
文章图片
objc_getMetaClass@2x.png 往元类中添加方法class_addMethod(objc_getMetaClass("LGPerson"), sel, imp, type);
走入m = getMethodNoSuper_nolock(cls, name),发现和方法的慢速查找过程中是用一个方法,传入的cls也都是元类,
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) { if (!cls) return NO; mutex_locker_t lock(runtimeLock); return ! addMethod(cls, name, imp, types ?: "", NO); } static IMP addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace) { IMP result = nil; runtimeLock.assertLocked(); checkIsKnownClass(cls); ASSERT(types); ASSERT(cls->isRealized()); method_t *m; if ((m = getMethodNoSuper_nolock(cls, name))) {//表示在LGPerson的元类中已经存在name // already exists if (!replace) {//不用传入的imp进行替换 result = m->imp; } else {//用传入的imp进行替换 result = _method_setImplementation(cls, m, imp); } } else { auto rwe = cls->data()->extAllocIfNeeded(); // fixme optimize method_list_t *newlist; newlist = (method_list_t *)calloc(sizeof(*newlist), 1); newlist->entsizeAndFlags = (uint32_t)sizeof(method_t) | fixed_up_method_list; newlist->count = 1; newlist->first.name = name; newlist->first.types = strdupIfMutable(types); newlist->first.imp = imp; prepareMethodLists(cls, &newlist, 1, NO, NO); rwe->methods.attachLists(&newlist, 1); flushCaches(cls); result = nil; }return result; }

iOS-底层原理10-动态方法决议&消息转发
文章图片
getMethodNoSuper_nolock@2x.png 传入cls在LGPerson的元类中进行查找,若查找到,是否替换,替换用传入的imp替换,
iOS-底层原理10-动态方法决议&消息转发
文章图片
getMethodNoSuper_nolock查找@2x.png result = _method_setImplementation(cls, m, imp); 不替换返回原来的m->imp,
iOS-底层原理10-动态方法决议&消息转发
文章图片
LGPerson元类添加方法imp@2x.png 若没找到则在LGPerson的元类cls的data()中进行增加内存空间,重新添加auto rwe = cls->data()->extAllocIfNeeded(); rwe->methods.attachLists(&newlist, 1); 实时更新类的信息flushCaches(cls); 方便下次查找,所以能找到。不会再报unrecognized selector sent to instance 0x10102c040找不到方法的错了。
iOS-底层原理10-动态方法决议&消息转发
文章图片
类方法动态方法决议@2x.png 以上是通过重写resolveClassMethod:方法,添加Imp的方式对类方法进行动态方法决议,类方法还能通过重写+ (BOOL)resolveInstanceMethod:(SEL)sel的方式进行动态方法决议吗??? iOS-底层原理10-动态方法决议&消息转发
文章图片
类方法的动态方法决议resolveInstanceMethod@2x.png 2.重写+ (BOOL)resolveInstanceMethod:(SEL)sel
理所当然的会想到重写+ (BOOL)resolveInstanceMethod:(SEL)sel方法,将上面添加元类方法的Imp写入 (BOOL)resolveInstanceMethod:(SEL)sel中
iOS-底层原理10-动态方法决议&消息转发
文章图片
resolveInstanceMethod中添加LGPerson元类方法@2x.png 发现添加的方法没有生效,依然报错,为什么会报错呢?已经往元类中添加过方法了,为什么没有找到呢?
2020-11-13 18:10:21.604474+0800 KCObjc[50067:1891886] +[LGPerson sayNB]: unrecognized selector sent to class 0x1000032d8 2020-11-13 18:10:21.607479+0800 KCObjc[50067:1891886] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[LGPerson sayNB]: unrecognized selector sent to class 0x1000032d8' *** First throw call stack: ( 0CoreFoundation0x00007fff374a8b57 __exceptionPreprocess + 250 1libobjc.A.dylib0x00000001001318fa objc_exception_throw + 42 2CoreFoundation0x00007fff37527b37 __CFExceptionProem + 0 3CoreFoundation0x00007fff3740d3bb ___forwarding___ + 1427 4CoreFoundation0x00007fff3740cd98 _CF_forwarding_prep_0 + 120 5KCObjc0x0000000100001a53 main + 67 6libdyld.dylib0x00007fff71497cc9 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException

iOS-底层原理10-动态方法决议&消息转发
文章图片
类方法动态方法决议resolveInstanceMethod添加元类LGPerson方法失败报错@2x.png 通过断点调试,查看进入resolveInstanceMethod方法中的cls为LGPerson的元类,通过lldb查看LGPerson的元类中存在lgClassMethod和resolveInstanceMethod:两个方法,并不存在sayNB方法
这句代码为falsebool resolved = msg(cls, resolve_sel, sel);
iOS-底层原理10-动态方法决议&消息转发
文章图片
cls为元类@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
LGPerson元类中的方法@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
resolved为false@2x.png 为什么LGPerson的类方法resolveInstanceMethod:中添加的元类方法没有生效呢?
因为LGPerson的元类是一个虚拟的类,在代码中并不存在,所以在LGPerson的.m文件中增加resolveInstanceMethod:方法,程序并不会真正进入,就不会执行,所以sayNB方法就没有添加进去,从而报错,此时没有生效的话,可以根据继承链的关系LPerson元类->LGPerson根元类->NSObject->nil,NSObject不是一个虚拟的类,是一个实实在在的的类,可以新建类别NSObject+LG,将方法写入到NSObject+LG中,增加resolveInstanceMethod:,断点调试看是否能生效。
NSObject中本身存在resolveInstanceMethod:方法,重写此方法,返回值保持一致。return NO。
iOS-底层原理10-动态方法决议&消息转发
文章图片
NSObject中的resolveInstanceMethod@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
NSObjec+LG中增加resolveInstanceMethod@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
resolve为true@2x.png 说明NSObject+LG中的resolveInstanceMethod:方法生效,已经将sayNB的替代方法lgClassMethod添加到LGPerson的元类中,不会再报错。
iOS-底层原理10-动态方法决议&消息转发
文章图片
类方法通过resolveInstanceMethod动态方法决议@2x.png 结论:
NSObject+LG是系统的分类,可能会多一些系统的方法会受影响
可以通过过滤特定命名规则的代码找不到的情况,做上传服务器操作或跳转到首页或特定的页面,防止程序崩溃
AOP封装成SDK,一般在这一层不作处理,而进行消息转发
消息转发 1.快速转发流程
instrumentObjcMessageSends辅助分析 在消息慢速转发流程中IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior),若找到了则进入log_and_fill_cache,满足条件slowpath(objcMsgLogEnabled && implementer)进入logMessageSend,对方法进行打印记录,详细记录到文件路径下/tmp/msgSends
iOS-底层原理10-动态方法决议&消息转发
文章图片
lookUpImpOrForward中的log_and_fill_cache@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
log_and_fill_cache方法@2x.png 通过instrumentObjcMessageSends改变objcMsgLogEnabled的值为true,从而记录调用流程,在外部使用内部的方法需要添加关键字extern。extern void instrumentObjcMessageSends(BOOL flag);
iOS-底层原理10-动态方法决议&消息转发
文章图片
instrumentObjcMessageSends@2x.png 在LGPerson中添加一个没有实现的对象方法sayHello,查看方法调用情况,在/tmp/msgSends路径下会生成一个名为msgSends-41550的文件,查看文件中的内容即为方法的调用流程,发现调用了- LGPerson NSObject forwardingTargetForSelector:方法
iOS-底层原理10-动态方法决议&消息转发
文章图片
forwardingTargetForSelector官方文档@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
instrumentObjcMessageSends记录方法调用流程@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
msgSends消息记录@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
forwardingTargetForSelector@2x.png 此时可以在LGPerson中重写forwardingTargetForSelector:方法,程序崩溃之前打印了sayHello方法说明可以在此方法内将有sayHello方法实现的类返回或在此方法内添加sayHello的Imp。
iOS-底层原理10-动态方法决议&消息转发
文章图片
forwardingTargetForSelector重写@2x.png
  • 1.forwardingTargetForSelector:方法中将有sayHello方法实现的类返回
iOS-底层原理10-动态方法决议&消息转发
文章图片
LGStudent添加对象方法sayHello@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
消息快速转发有实现sayHello对象方法的类LGStudent@2x.png
// 1: 快速转发 - (id)forwardingTargetForSelector:(SEL)aSelector{ NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector)); // runtime + aSelector + addMethod + imp //return [super forwardingTargetForSelector:aSelector]; return [LGStudent alloc]; }

  • 2.forwardingTargetForSelector:方法中动态添加sayHello的Imp,结果程序崩溃,为什么呢???
iOS-底层原理10-动态方法决议&消息转发
文章图片
快速转发通过runtime添加Imp@2x.png 2.慢速转发流程
在msgSends-41550的文件中发现在forwardingTargetForSelector:方法之后,还调用了- LGPerson NSObject methodSignatureForSelector:方法
iOS-底层原理10-动态方法决议&消息转发
文章图片
methodSignatureForSelector@2x.png methodSignatureForSelector官方文档@2x.png 查阅官方文档- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector和- (void)forwardInvocation:(NSInvocation *)anInvocation要搭配使用,有两种方式,一种是重写方法,但是不做处理,另一种是进行事务的重新赋值
  • 1.重写- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector和- (void)forwardInvocation:(NSInvocation *)anInvocation方法,不做处理,仅仅防止奔溃
iOS-底层原理10-动态方法决议&消息转发
文章图片
消息慢速转发@2x.png
  • 2.事务重新赋值anInvocation.target = [LGStudent alloc]; [anInvocation invoke]; 对事物进行revoke。
iOS-底层原理10-动态方法决议&消息转发
文章图片
事务invoke@2x.png 大胆做个假设可以将事务进行本类方法指定吗,是可以的
iOS-底层原理10-动态方法决议&消息转发
文章图片
事务invoke@2x.png 消息转发流程图如下
iOS-底层原理10-动态方法决议&消息转发
文章图片
消息转发机制.png
以上是以上帝视角探索消息转发流程,有没有更好的办法呢?
反汇编探索消息转发流程 【iOS-底层原理10-动态方法决议&消息转发】程序崩溃后通过bt查看堆栈信息,在程序崩溃之前调用了CoreFoundation中的forwarding_prep_0forwarding,寻找CoreFoundation的源码,在官网源码查找并下载CF-1151.16.tar,在源码中查找forwarding_prep_0,发现无法找到。
iOS-底层原理10-动态方法决议&消息转发
文章图片
CoreFoundation__forwarding_prep_0___@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
CF-1151.16@2x.png
  • 读取CorFoundation镜像文件,路径为/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation,找到CoreFoundation的可执行文件
iOS-底层原理10-动态方法决议&消息转发
文章图片
imagelist@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
CoreFoundation可执行文件@2x.png
  • 通过工具Hopper.Demo.dmg查看编译后的代码,付费软件使用试用版try the demo
iOS-底层原理10-动态方法决议&消息转发
文章图片
CoreFoundation可执行文件拖入Hopper@2x.png iOS-底层原理10-动态方法决议&消息转发
文章图片
Mach-O64bits@2x.png
  • 全局搜索__forwarding_prep_0___,点击跳转到函数入口
Hopper__forwarding_prep_0__@2x.png ___forwarding_prep_0__伪代码模式@2x.png ___forwarding__@2x.png
  • 进入__forwarding__流程图如下,和前面探索的消息转发流程不谋而合。
iOS-底层原理10-动态方法决议&消息转发
文章图片
Hopper反编译消息转发流程.png

    推荐阅读