我们先从lookUpImpOrForward看起
realizeAndInitializeIfNeeded_locked jump
![]()
initializeAndLeaveLocked jump
![]()
initializeAndMaybeRelock jump
![]()
initializeNonMetaClass jump
![]()
[图片上传失败...(image-122663-1625218937103)]
callInitialize jump
![]()
callInitialize 会把当前的class的initialize进行系统调用
二分法查找
![]()
举例现在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)
坑点
![]()
1、对象调用一个类方法,是可以成功的,只要在NSObject里处理,在分类添加,根本原因是isa的走位图。
不写m文件里的实现,会报错如下
![]()
- _objc_msgForward_impcache
- __objc_forward_handler
//重点代码
_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);
动态方法决议
// 单例方法只会执行一次
//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);
}
再次动态默认赋值
![]()
behavior 来源
![]()
当方法没有写实现,缓存查找及慢速的递归流程查找后,还是没找到,会导致崩溃!!!
![]()
再给最后一个机会,重新开始查找lookUpImpOrForwardTryCache,返回imp
return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
#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
![]()
- 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添加如下代码
![]()
在程序报错之前,走了上面定义的方法,输出如下
+ (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
系统默认实现,系统会帮你兜底。
![]()
类方法的动态决议
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
nonmeta 当前元类
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'
resolveClassMethod : HLTeacher-encodeWithOSLogCoder:options:maxLength:
HLTeacher - +[HLTeacher sayHello]
未完待续......
网友评论