消息动态决议

我们先从lookUpImpOrForward看起

截屏2021-07-01 下午3.22.34.png

  • realizeAndInitializeIfNeeded_locked jump

截屏2021-07-01 下午3.24.23.png

  • initializeAndLeaveLocked jump

截屏2021-07-01 下午3.25.35.png

  • initializeAndMaybeRelock jump

截屏2021-07-01 下午3.26.07.png

  • initializeNonMetaClass jump

截屏2021-07-01 下午3.27.55.png
截屏2021-07-01 下午3.30.47.png

  • callInitialize jump

截屏2021-07-01 下午3.32.45.png

callInitialize 会把当前的class的initialize进行系统调用

二分法查找

截屏2021-07-01 下午3.46.10.png

举例现在1-8共8个方法,count=8,假设现在正确的方法在7号位置,当前0号位置开始,开始右移

base + (count >> 1) = 0 + (8 >> 1) = 1000 >> 1 = 0100 = 4 //当前位置  
base = probe + 1 = 4 + 1 = 5;//当前位置
base = probe + 1 = 5 + 1 = 6;//当前位置 
base = probe + 1 = 5 + 1 = 7;//当前位置  

count -- = 8 - 1 = 7;
count = 7 >> 1 = 0111 >> 1 = 0011 = 3
count = 6 >> 1 = 0011 >> 1 = 0001 = 1
count -- = 3 - 1 = 2;
count = 2 >> 1 = 0010 >> 1 = 0001 = 1
1 >> 1 = 0001 >> 1 = 0
复制代码
  • 查找流程:自己的类->找父类->缓存查找->lookupimp->父类的缓存(cache)

坑点

截屏2021-07-01 下午5.20.01.png

1、对象调用一个类方法,是可以成功的,只要在NSObject里处理,在分类添加,根本原因是isa的走位图。

截屏2021-07-02 上午9.47.25.png

不写m文件里的实现,会报错如下

截屏2021-07-02 上午9.47.55.png

截屏2021-07-02 上午9.50.45.png

截屏2021-07-02 上午9.51.33.png

  • _objc_msgForward_impcache

截屏2021-07-02 上午9.53.24.png

  • __objc_forward_handler

截屏2021-07-02 上午9.57.44.png

//重点代码
  _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);
复制代码

动态方法决议

截屏2021-07-02 上午10.04.40.png

截屏2021-07-02 上午11.31.52.png

// 单例方法只会执行一次
//3 & 2 = 0011 & 0010 = 0010 = 2
//2 & 2 = 0 = behavior
if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }
复制代码

再次动态默认赋值

截屏2021-07-02 上午10.31.17.png

behavior 来源

截屏2021-07-02 上午10.16.35.png

当方法没有写实现,缓存查找及慢速的递归流程查找后,还是没找到,会导致崩溃!!!

截屏2021-07-02 上午10.33.47.png

再给最后一个机会,重新开始查找lookUpImpOrForwardTryCache,返回imp

return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);

截屏2021-07-02 上午10.42.22.png


#if CONFIG_USE_PREOPT_CACHES
    if (fastpath(cls->cache.isConstantOptimizedCache(/* strict */true))) {
        imp = cache_getImp(cls->cache.preoptFallbackClass(), sel);
    }
#endif
 if (slowpath(imp == NULL)) {
        return lookUpImpOrForward(inst, sel, cls, behavior);
    }
   
   再次执行lookUpImpOrForward
   满足下面的条件,可以再找一次
   //判断是不是元类
       if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
            resolveInstanceMethod(inst, sel, cls);
        }
    }
复制代码
  • resolveInstanceMethod jump

截屏2021-07-02 下午2.23.34.png


 - resolve_sel 系统发送这个消息
执行 IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
在这里执行了两次TryCache
//只要使用了下面的方法 SEL resolve_sel = @selector(resolveInstanceMethod:), 就不会报错
 BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, sel);
复制代码

.m添加如下代码

截屏2021-07-02 下午2.35.41.png

在程序报错之前,走了上面定义的方法,输出如下

截屏2021-07-02 下午2.37.54.png


+ (BOOL)resolveInstanceMethod:(SEL)sel{
    
    if (sel == @selector(sayNB)) {
        
        IMP teacherSayImp = class_getMethodImplementation(self, @selector(teacherSay));
        
        Method method = class_getInstanceMethod(self, @selector(teacherSay));
        
        const char *type = method_getTypeEncoding(method);
        
        return  class_addMethod(self,sel,teacherSayImp,type);
    }
    
    NSLog(@"resolveInstanceMethod : %@-%@",self,NSStringFromSelector(sel));
 
    return [super resolveInstanceMethod:sel];
    
}

 //替换 输出 
 -[HLTeacher teacherSay]
Program ended with exit code: 0
复制代码

系统默认实现,系统会帮你兜底。

截屏2021-07-02 下午3.42.59.png

类方法的动态决议

截屏2021-07-02 下午3.51.11.png

截屏2021-07-02 下午3.52.58.png

截屏2021-07-02 下午4.02.56.png

 BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
 bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
复制代码

nonmeta 当前元类

截屏2021-07-02 下午4.12.30.png

resolveClassMethod : HLTeacher-encodeWithOSLogCoder:options:maxLength:
resolveClassMethod : HLTeacher-sayHappy
+[HLTeacher sayHappy]: unrecognized selector sent to class 0x100008508
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[HLTeacher sayHappy]: unrecognized selector sent to class 0x100008508'

复制代码

截屏2021-07-02 下午4.47.40.png

resolveClassMethod : HLTeacher-encodeWithOSLogCoder:options:maxLength:
HLTeacher - +[HLTeacher sayHello]
复制代码

未完待续……

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享