IMG_5556.JPG

补充

慢速查找流程的realizeAndInitializeIfNeeded_locked方法会主动调用class的initialize方法,此方法在+load、initalize、C++构造方法时自动调用,其流程如下
realizeAndInitializeIfNeeded_locked -> realizeAndInitializeIfNeeded_locked -> initializeAndLeaveLocked -> initializeAndMaybeRelock -> initializeNonMetaClass -> callInitialize

  1. void callInitialize(Class cls)
  2. {
  3. ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
  4. asm("");
  5. }

动态方法决议

方法找不到的底层原理

开发过程中,经常会遇到经典的unrecongnize selector,其底层是如何实现的?
在lookUpImpOrForward中,初始值是_objc_msgForward_impcache

  1. NEVER_INLINE
  2. IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
  3. {
  4. const IMP forward_imp = (IMP)_objc_msgForward_impcache;

点击查看,跳入汇编代码,objc_msgForward_impcache -> objc_msgForward -> __objc_forward_handler

  1. STATIC_ENTRY __objc_msgForward_impcache
  2. // No stret specialization.
  3. b __objc_msgForward
  4. END_ENTRY __objc_msgForward_impcache
  5. ENTRY __objc_msgForward
  6. adrp x17, __objc_forward_handler@PAGE
  7. ldr p17, [x17, __objc_forward_handler@PAGEOFF]
  8. TailCallFunctionPointer x17
  9. END_ENTRY __objc_msgForward

__objc_forward_handler又跳转C++代码,在objc_defaultForwardHandler底层打印实现了unrecongnice selector函数

  1. void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
  2. objc_defaultForwardHandler(id self, SEL sel)
  3. {
  4. _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
  5. "(no message forward handler is installed)",
  6. class_isMetaClass(object_getClass(self)) ? '+' : '-',
  7. object_getClassName(self), sel_getName(sel), self);
  8. }

流程图:
未命名文件-7.jpg

对象方法动态决议

在lookUpImpOrForward函数中,break跳出for循环后,执行以下单例方法behavior = 3,LOOKUP_RESOLVER = 2, 3 & 2 = 2, 3 ^ 2 = 0, 0 赋值给behavior,保证只走一次

  1. if (slowpath(behavior & LOOKUP_RESOLVER)) { //
  2. behavior ^= LOOKUP_RESOLVER;
  3. return resolveMethod_locked(inst, sel, cls, behavior);
  4. }

resolveMethod_locked中会根据判断会调用resolveInstanceMethod或者resolveClassMethod,resolveClassMethod中多了lookUpImpOrNilTryCache判断,目前是查找当前类的对象是否显示的imp

  1. resolveMethod_locked(id inst, SEL sel, Class cls, int behavior) {
  2. if (cls是元类) {
  3. resolveClassMethod();
  4. if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
  5. resolveInstanceMethod(inst, sel, cls);
  6. }
  7. } else {
  8. resolveInstanceMethod(inst, sel, cls);
  9. }
  10. }

resolveInstanceMethod 会调用msg(cls, resolve_sel, sel),cls是类,所以resolveInstanceMethod是+方法

  1. static void resolveInstanceMethod(id inst, SEL sel, Class cls)
  2. {
  3. runtimeLock.assertUnlocked();
  4. ASSERT(cls->isRealized());
  5. SEL resolve_sel = @selector(resolveInstanceMethod:);
  6. if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
  7. // Resolver not implemented.
  8. return;
  9. }
  10. BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
  11. bool resolved = msg(cls, resolve_sel, sel);
  12. // Cache the result (good or bad) so the resolver doesn't fire next time.
  13. // +resolveInstanceMethod adds to self a.k.a. cls
  14. IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
  15. if (resolved && PrintResolving) {
  16. if (imp) {
  17. _objc_inform("RESOLVE: method %c[%s %s] "
  18. "dynamically resolved to %p",
  19. cls->isMetaClass() ? '+' : '-',
  20. cls->nameForLogging(), sel_getName(sel), imp);
  21. }
  22. else {
  23. // Method resolver didn't add anything?
  24. _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
  25. ", but no new implementation of %c[%s %s] was found",
  26. cls->nameForLogging(), sel_getName(sel),
  27. cls->isMetaClass() ? '+' : '-',
  28. cls->nameForLogging(), sel_getName(sel));
  29. }
  30. }
  31. }

举例

定义一个teacher类,继承于person类,person类声明了say666方法,teacher类和person类都未实现该方法,调用[[teacher alloc] say666]运行程序是会崩溃,unrecongnice selector

  1. -[LGTeacher say666]: unrecognized selector sent to instance 0x108d0bdb0

在LGTeacher类中,重写resolveInstanceMethod后,添加断点调试,可知在缓存查找失败后,对象方法调用以下函数+(BOOL)resolveInstanceMethod:(SEL)sel,重写resolveInstanceMethod,动态添加新sel sayNB后,可以防止程序崩溃

  1. + (BOOL)resolveInstanceMethod:(SEL)sel {
  2. if (sel == @selector(say666)) {
  3. IMP sayNBImp = class_getMethodImplementation(self, @selector(sayNB));
  4. Method method = class_getInstanceMethod(self, @selector(sayNB));
  5. const char *type = method_getTypeEncoding(method);
  6. return class_addMethod(self, sel, sayNBImp, type);
  7. }
  8. NSLog(@"resolveInstanceMethod :%@-%@",self,NSStringFromSelector(sel));
  9. return [super resolveInstanceMethod:sel];
  10. }
  11. <LGTeacher: 0x108b503f0> - -[LGTeacher sayNB]

类方法也可以做类似操作,在类中类的方式相当于元类中的对象方式

  1. //// 元类的以对象方法的方法
  2. + (BOOL)resolveClassMethod:(SEL)sel{
  3. NSLog(@"resolveClassMethod :%@-%@",self,NSStringFromSelector(sel));
  4. if (sel == @selector(sayHappy)) {
  5. IMP sayNBImp = class_getMethodImplementation(objc_getMetaClass("LGTeacher"), @selector(sayKC));
  6. Method method = class_getInstanceMethod(objc_getMetaClass("LGTeacher"), @selector(sayKC));
  7. const char *type = method_getTypeEncoding(method);
  8. return class_addMethod(objc_getMetaClass("LGTeacher"), sel, sayNBImp, type);
  9. }
  10. return [super resolveClassMethod:sel];
  11. }

难点

在打印过程中,我们发现无论是resolveClassMethod还是resolveInstanceMethod都打印了两次,这是怎么回事呢?
在resolveInstanceMethod我们加入断点,po当前sel得到say666

  1. bool resolved = msg(cls, resolve_sel, sel);

过掉断点,在第二次断点时,po sel再次的到say666,此时bt查看函数调用栈,CFforwardingprep_0 -> forwarding_prep_0 -> [NSObject methodSignatureForSelector]
image.png
第一次调用resolveInstanceMethod,是走的慢速查找的动态决议。
第二次调用resolveInstanceMethod的堆栈信息,由底层系统库CoreFoundation调起,在消息转发完成以后再次开启慢速查找流程,进入动态方法决议又调用一次resolveInstanceMethod,所以总共是两次。
通过hopper查看coreFundation库,
forwarding
是一个回调,系统给了一次机会,再调用lookUpImpOrForward
image.png
3505EE0B-D33B-4DBD-A759-980FC3B512C4.png