Method could not find the cause of the error
During a slow lookup, a forward_IMP is returned if the final lookup is not found
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
Copy the code
Search globally for _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
Copy the code
__objc_forward_handler = __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)) ? '+' : The '-',
object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
Copy the code
You can find the error printing function objc_defaultForwardHandler.
Dynamic method resolution
Behavior is 3, LOOKUP_RESOLVER is 2, behavior & LOOKUP_RESOLVER is combined with behavior ^= LOOKUP_RESOLVER to realize singleton mode. You can only enter once.
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
Copy the code
Check the resolveMethod_locked function
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);
}
Copy the code
resolveInstanceMethod
If! CLS ->isMetaClass() is not a metaclass, that is, the object method, into the resolveInstanceMethod function
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)); }}}Copy the code
As you can see, the system actively sends a message to the resolveInstanceMethod function
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, resolve_sel, sel);
Copy the code
If the resolveInstanceMethod function returns an IMP to sel, then the program can fix it
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
Copy the code
resolveInstanceMethod
implementation
Add a class LKPerson, add the method sayHello, but do not implement it. Implementation method
- (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];
}
Copy the code
You can see that the program doesn’t crash, and print teacherSayHello.
resolveClassMethod
If! CLS ->isMetaClass() ->isMetaClass() ->isMetaClass(
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() ? '+' : The '-',
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() ? '+' : The '-', cls->nameForLogging(), sel_getName(sel)); }}}Copy the code
ResolveClassMethod is similar to the resolveInstanceMethod function
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);
Copy the code
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst); According to the metaclass, get the current class, send the resolveClassMethod message to the class, and then look for imp again.
resolveClassMethod
implementation
Add instance methods, but do not implement them.
+ (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];
}
Copy the code
Print classTeacherSayHello.