补充
慢速查找流程的realizeAndInitializeIfNeeded_locked方法会主动调用class的initialize方法,此方法在+load、initalize、C++构造方法时自动调用,其流程如下realizeAndInitializeIfNeeded_locked -> realizeAndInitializeIfNeeded_locked -> initializeAndLeaveLocked -> initializeAndMaybeRelock -> initializeNonMetaClass -> callInitialize
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}
动态方法决议
方法找不到的底层原理
开发过程中,经常会遇到经典的unrecongnize selector,其底层是如何实现的?
在lookUpImpOrForward中,初始值是_objc_msgForward_impcache
NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
点击查看,跳入汇编代码,objc_msgForward_impcache -> objc_msgForward -> __objc_forward_handler
STATIC_ENTRY __objc_msgForward_impcache
// No stret specialization.
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward
adrp x17, __objc_forward_handler@PAGE
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
TailCallFunctionPointer x17
END_ENTRY __objc_msgForward
__objc_forward_handler又跳转C++代码,在objc_defaultForwardHandler底层打印实现了unrecongnice selector函数
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
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);
}
对象方法动态决议
在lookUpImpOrForward函数中,break跳出for循环后,执行以下单例方法behavior = 3,LOOKUP_RESOLVER = 2, 3 & 2 = 2, 3 ^ 2 = 0, 0 赋值给behavior,保证只走一次
if (slowpath(behavior & LOOKUP_RESOLVER)) { //
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
resolveMethod_locked中会根据判断会调用resolveInstanceMethod或者resolveClassMethod,resolveClassMethod中多了lookUpImpOrNilTryCache判断,目前是查找当前类的对象是否显示的imp
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior) {
if (cls是元类) {
resolveClassMethod();
if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
resolveInstanceMethod(inst, sel, cls);
}
} else {
resolveInstanceMethod(inst, sel, cls);
}
}
resolveInstanceMethod 会调用msg(cls, resolve_sel, sel),cls是类,所以resolveInstanceMethod是+方法
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
SEL resolve_sel = @selector(resolveInstanceMethod:);
if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, resolve_sel, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
举例
定义一个teacher类,继承于person类,person类声明了say666方法,teacher类和person类都未实现该方法,调用[[teacher alloc] say666]运行程序是会崩溃,unrecongnice selector
-[LGTeacher say666]: unrecognized selector sent to instance 0x108d0bdb0
在LGTeacher类中,重写resolveInstanceMethod后,添加断点调试,可知在缓存查找失败后,对象方法调用以下函数+(BOOL)resolveInstanceMethod:(SEL)sel,重写resolveInstanceMethod,动态添加新sel sayNB后,可以防止程序崩溃
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(say666)) {
IMP sayNBImp = class_getMethodImplementation(self, @selector(sayNB));
Method method = class_getInstanceMethod(self, @selector(sayNB));
const char *type = method_getTypeEncoding(method);
return class_addMethod(self, sel, sayNBImp, type);
}
NSLog(@"resolveInstanceMethod :%@-%@",self,NSStringFromSelector(sel));
return [super resolveInstanceMethod:sel];
}
<LGTeacher: 0x108b503f0> - -[LGTeacher sayNB]
类方法也可以做类似操作,在类中类的方式相当于元类中的对象方式
//// 元类的以对象方法的方法
+ (BOOL)resolveClassMethod:(SEL)sel{
NSLog(@"resolveClassMethod :%@-%@",self,NSStringFromSelector(sel));
if (sel == @selector(sayHappy)) {
IMP sayNBImp = class_getMethodImplementation(objc_getMetaClass("LGTeacher"), @selector(sayKC));
Method method = class_getInstanceMethod(objc_getMetaClass("LGTeacher"), @selector(sayKC));
const char *type = method_getTypeEncoding(method);
return class_addMethod(objc_getMetaClass("LGTeacher"), sel, sayNBImp, type);
}
return [super resolveClassMethod:sel];
}
难点
在打印过程中,我们发现无论是resolveClassMethod还是resolveInstanceMethod都打印了两次,这是怎么回事呢?
在resolveInstanceMethod我们加入断点,po当前sel得到say666
bool resolved = msg(cls, resolve_sel, sel);
过掉断点,在第二次断点时,po sel再次的到say666,此时bt查看函数调用栈,CFforwardingprep_0 -> forwarding_prep_0 -> [NSObject methodSignatureForSelector]
第一次调用resolveInstanceMethod,是走的慢速查找的动态决议。
第二次调用resolveInstanceMethod的堆栈信息,由底层系统库CoreFoundation调起,在消息转发完成以后再次开启慢速查找流程,进入动态方法决议又调用一次resolveInstanceMethod,所以总共是两次。
通过hopper查看coreFundation库,forwarding是一个回调,系统给了一次机会,再调用lookUpImpOrForward