我們先從lookUpImpOrForward看起
realizeAndInitializeIfNeeded_locked jump
initializeAndLeaveLocked jump
initializeAndMaybeRelock jump
initializeNonMetaClass jump
[圖片上傳失敗...(image-122663-1625218937103)]
callInitialize jump
callInitialize 會把當(dāng)前的class的initialize進(jìn)行系統(tǒng)調(diào)用
二分法查找
舉例現(xiàn)在1-8共8個方法,count=8,假設(shè)現(xiàn)在正確的方法在7號位置,當(dāng)前0號位置開始,開始右移
base + (count >> 1) = 0 + (8 >> 1) = 1000 >> 1 = 0100 = 4 //當(dāng)前位置
base = probe + 1 = 4 + 1 = 5;//當(dāng)前位置
base = probe + 1 = 5 + 1 = 6;//當(dāng)前位置
base = probe + 1 = 5 + 1 = 7;//當(dāng)前位置
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)
坑點(diǎn)
1、對象調(diào)用一個類方法,是可以成功的漓帚,只要在NSObject里處理,在分類添加午磁,根本原因是isa的走位圖尝抖。
不寫m文件里的實(shí)現(xiàn)毡们,會報錯如下
- _objc_msgForward_impcache
- __objc_forward_handler
//重點(diǎn)代碼
_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);
動態(tài)方法決議
// 單例方法只會執(zhí)行一次
//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);
}
再次動態(tài)默認(rèn)賦值
behavior 來源
當(dāng)方法沒有寫實(shí)現(xiàn),緩存查找及慢速的遞歸流程查找后昧辽,還是沒找到漏隐,會導(dǎo)致崩潰!奴迅!!
再給最后一個機(jī)會青责,重新開始查找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);
}
再次執(zhí)行l(wèi)ookUpImpOrForward
滿足下面的條件取具,可以再找一次
//判斷是不是元類
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 系統(tǒng)發(fā)送這個消息
執(zhí)行 IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
在這里執(zhí)行了兩次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
系統(tǒng)默認(rèn)實(shí)現(xiàn)产阱,系統(tǒng)會幫你兜底。
類方法的動態(tài)決議
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
nonmeta 當(dāng)前元類
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]
未完待續(xù)......