方法查找不到报错原因
慢速查找过程中,如果最终查找不到,会返回一个forward_imp
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
复制代码
全局搜索_objc_msgForward_impcache
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
,全局搜索__objc_forward_handler
,搜索不到,去掉__
再次搜索,
__attribute__((noreturn, cold)) void
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);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
复制代码
可以发现错误打印函数objc_defaultForwardHandler
。
动态方法决议
慢速查找流程中,如果查找不到,会进入下面流程behavior
为3,LOOKUP_RESOLVER
为2,behavior & LOOKUP_RESOLVER
和behavior ^= LOOKUP_RESOLVER
结合,实现单例模式,只能进入一次。
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
复制代码
查看resolveMethod_locked
函数
static NEVER_INLINE IMP resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
runtimeLock.unlock();
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);
}
}
return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}
复制代码
resolveInstanceMethod
如果! cls->isMetaClass()
不是元类
,即对象方法
,进入resolveInstanceMethod
函数
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));
}
}
}
复制代码
可以看到系统会主动给resolveInstanceMethod
函数发送消息
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, resolve_sel, sel);
复制代码
然后再次进入缓存查找imp
,如果我们在resolveInstanceMethod
函数给sel
返回一个imp
,是否程序就可以补救
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
复制代码
resolveInstanceMethod
实现
添加一个类LKPerson
,添加方法sayHello
,但不实现。实现方法
- (void)teacherSayHello{
NSLog(@"%@ - %s",self , __func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"resolveInstanceMethod :%@-%@",self,NSStringFromSelector(sel));
if (sel == @selector(sayHello)) {
IMP sayHelloImp = class_getMethodImplementation(self, @selector(teacherSayHello));
Method method = class_getInstanceMethod(self, @selector(teacherSayHello));
const char *type = method_getTypeEncoding(method);
return class_addMethod(self, sel, sayHelloImp, type);
}
return [super resolveInstanceMethod:sel];
}
复制代码
可以看到程序并没有崩溃,并且打印teacherSayHello
。
resolveClassMethod
如果! cls->isMetaClass()
是元类
,即实例方法
,进入resolveClassMethod
函数
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
ASSERT(cls->isMetaClass());
if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)){
// Resolver not implemented.
return;
}
Class nonmeta;
{
mutex_locker_t lock(runtimeLock);
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// +initialize path should have realized nonmeta already
if (!nonmeta->isRealized()) {
_objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
nonmeta->nameForLogging(), nonmeta);
}
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() 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 resolveClassMethod:%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));
}
}
}
复制代码
resolveClassMethod
与resolveInstanceMethod
函数类似
Class nonmeta;
{
mutex_locker_t lock(runtimeLock);
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// +initialize path should have realized nonmeta already
if (!nonmeta->isRealized()) {
_objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
nonmeta->nameForLogging(), nonmeta);
}
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
复制代码
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
根据元类,获取当前类
向类
发送resolveClassMethod
消息,然后再次查找imp
。
resolveClassMethod
实现
添加实例方法,但不实现。
+ (void)classSayHello;
+ (void)classTeacherSayHello{
NSLog(@"%@ - %s",self , __func__);
}
+ (BOOL)resolveClassMethod:(SEL)sel{
NSLog(@"resolveClassMethod :%@-%@",self,NSStringFromSelector(sel));
if (sel == @selector(classSayHello)) {
IMP sayHelloImp = class_getMethodImplementation(objc_getMetaClass("LKPerson"), @selector(classTeacherSayHello));
Method method = class_getInstanceMethod(objc_getMetaClass("LKPerson"), @selector(classTeacherSayHello));
const char *type = method_getTypeEncoding(method);
return class_addMethod(objc_getMetaClass("LKPerson"), sel, sayHelloImp, type);
}
return [super resolveClassMethod:sel];
}
复制代码
打印classTeacherSayHello
。