动态方法解析、消息转发
如果方法实现(imp)没有找到会尝试一次动态方法解析,源码如
if (resolver && !triedResolver) {
runtimeLock.unlockRead();
_class_resolveMethod(cls, sel, inst);
runtimeLock.read();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
动态方法解析调用 "_class_resolveMethod" 是实例方法调用 进入 "_class_resolveInstanceMethod(cls, sel, inst);", 类方法调用" _class_resolveClassMethod(cls, sel, inst);" 方法实现如图
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
_class_resolveClassMethod(cls, sel, inst);
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
_class_resolveInstanceMethod(cls, sel, inst);
}
}
}
尝试一次动态方法解析没有找到会调用消息转发,到此动态方法解析完成。没找到之后进行消息转发并将查找的IMP添加到缓存里,源码如
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
消息转发调用方法_objc_msgForward_impcache 回到汇编中,源码如
STATIC_ENTRY __objc_msgForward_impcache
MESSENGER_START
nop
MESSENGER_END_SLOW
// No stret specialization.
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward
adrp x17, __objc_forward_handler@PAGE
ldr x17, [x17, __objc_forward_handler@PAGEOFF]
br x17
END_ENTRY __objc_msgForward
进入方法首先跳转 __objc_msgForward 方法, 在跳转 __objc_forward_handler回到C方法 源码如
#if !__OBJC2__
// Default forward handler (nil) goes to forward:: dispatch.
void *_objc_forward_handler = nil;
void *_objc_forward_stret_handler = nil;
#else
// Default forward handler halts the process.
__attribute__((noreturn)) 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_forward_handler 实际是一个C方法,系统给出了一个默认实现,到这源码结束。消息转发调用过程可以通过 runTime消息打印API来实现
extern void instrumentObjcMessageSends(BOOL);
消息转发会有两种实现, 一种是
- (id)forwardingTargetForSelector:(SEL)aSelector {
// 可以在此方法里进行动态添加处理
return [super forwardingTargetForSelector:aSelector];
}
另一种是
/// 方法名注册
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [super methodSignatureForSelector:aSelector];
}
/// 在此方法里处理响应
- (void)forwardInvocation:(NSInvocation *)anInvocation {
}
第二种方法比较灵活,可以进行hook等操作。