1.方法决议
上文 方法慢速查找流程 中对lookUpImpOrForward
方法进行分析,当前sel
寻找imp
的过程中,当查找父类到NSObject
或者查到了类继承上限的时候,imp
被赋值forward_imp
,开始进行方法决议,这篇文章主要分析这个流程。
方法决议定义
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 (!lookUpImpOrNil(inst, sel, cls)) {
resolveInstanceMethod(inst, sel, cls);
}
}
// chances are that calling the resolver have populated the cache
// so attempt using it
return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}
如果当前类不是元类
,调用resolveInstanceMethod
,否则调用resolveClassMethod
再通过lookUpImpOrNil
判断调用resolveInstanceMethod
,最后再通过lookUpImpOrForward
寻找一遍。
具体流程图如下:
疑问:元类方法 调用resolveClassMethod 之后为什么又调用了resolveInstanceMethod?
因为 类方法
储存在元类
中,元类
方法又是元类
实例方法,所以需要元类对resolveInstanceMethod
进行决议。
2.消息转发
2.1 通过本地文件打印查看转发流程
在研究cache_insert
的方法的时候,有过一个log_and_fill_cache
方法
static void
log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
{
#if SUPPORT_MESSAGE_LOGGING
if (slowpath(objcMsgLogEnabled && implementer)) {
bool cacheIt = logMessageSend(implementer->isMetaClass(),
cls->nameForLogging(),
implementer->nameForLogging(),
sel);
if (!cacheIt) return;
}
#endif
cache_fill(cls, sel, imp, receiver);
}
里面的objcMsgLogEnabled
控制本地方法打印
bool logMessageSend(bool isClassMethod,
const char *objectsClass,
const char *implementingClass,
SEL selector)
{
char buf[ 1024 ];
// Create/open the log file
if (objcMsgLogFD == (-1))
{
snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());
if (objcMsgLogFD < 0) {
// no log file - disable logging
objcMsgLogEnabled = false;
objcMsgLogFD = -1;
return true;
}
}
// Make the log entry
snprintf(buf, sizeof(buf), "%c %s %s %s\n",
isClassMethod ? '+' : '-',
objectsClass,
implementingClass,
sel_getName(selector));
objcMsgLogLock.lock();
write (objcMsgLogFD, buf, strlen(buf));
objcMsgLogLock.unlock();
// Tell caller to not cache the method
return false;
}
// 1: objcMsgLogEnabled 控制开关
// 2: extern
void instrumentObjcMessageSends(BOOL flag)
{
bool enable = flag;
// Shortcut NOP
if (objcMsgLogEnabled == enable)
return;
// If enabling, flush all method caches so we get some traces
if (enable)
_objc_flush_caches(Nil);
// Sync our log file
if (objcMsgLogFD != -1)
fsync (objcMsgLogFD);
objcMsgLogEnabled = enable;
}
mian.m
中定义
extern void instrumentObjcMessageSends(BOOL flag);
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
instrumentObjcMessageSends(YES);
[person sayInstanceMethod]; //空方法没有实现
instrumentObjcMessageSends(NO);
}
return 0;
}
运行后查看/tmp/msgSends
路径文件
从文件中得知调用resolveInstanceMethod
方法后对forwardingTargetForSelector
和methodSignatureForSelector
方法进行了调用。
在LGPerson.m
中增加方法
@implementation LGPerson
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@"%s ---->sel = %s",__func__, sel);
return [super resolveInstanceMethod:sel];
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSLog(@"%s ---->sel = %s",__func__, aSelector);
return [super forwardingTargetForSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSLog(@"%s ---->sel = %s",__func__, aSelector);
return [super methodSignatureForSelector:aSelector];
}
@end
查看堆栈
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
frame #0: 0x00007fff336876e6 CoreFoundation`CFStringGetLength + 6
frame #1: 0x00007fff336b1816 CoreFoundation`CFStringAppend + 229
frame #2: 0x00007fff336f1e4b CoreFoundation`-[NSArray componentsJoinedByString:] + 309
frame #3: 0x00007fff337f795f CoreFoundation`__handleUncaughtException + 761
frame #4: 0x00007fff6c5fa5a3 libobjc.A.dylib`_objc_terminate() + 90
frame #5: 0x00007fff6aacd887 libc++abi.dylib`std::__terminate(void (*)()) + 8
frame #6: 0x00007fff6aad01a2 libc++abi.dylib`__cxxabiv1::failed_throw(__cxxabiv1::__cxa_exception*) + 27
frame #7: 0x00007fff6aad0169 libc++abi.dylib`__cxa_throw + 113
frame #8: 0x00007fff6c5f86ed libobjc.A.dylib`objc_exception_throw + 350
frame #9: 0x00007fff337fdbe7 CoreFoundation`-[NSObject(NSObject) doesNotRecognizeSelector:] + 132
frame #10: 0x00007fff336e33bb CoreFoundation`___forwarding___ + 1427
frame #11: 0x00007fff336e2d98 CoreFoundation`__forwarding_prep_0___ + 120
* frame #12: 0x0000000100003b40 002-instrumentObjcMessageSends辅助分析`main(argc=1, argv=0x00007ffeefbff370) at main.m:18:9
frame #13: 0x00007fff6d7a0cc9 libdyld.dylib`start + 1
___forwarding___
之后调用了-[NSObject(NSObject) doesNotRecognizeSelector:]
方法,程序出现了报错,并没有显示forwardingTargetForSelector
方法调用,找下__forwarding_prep_0___
和___forwarding___
方法的调用,通过image list
命令在控制台找出CoreFoundation
库的位置,用ida
软件打开。
__forwarding_prep_0___
方法中调用了___forwarding___
,继续看。
void *__fastcall ___forwarding___(__int64 a1, __int64 a2)
{
__int64 v3; // rax
void *(**v4)(void *, const char *, ...); // r12
void *v5; // rbx
const char *v6; // r13
unsigned int v7; // eax
__int64 v8; // r12
void *v9; // rax
unsigned int v10; // ecx
const __CFString *v11; // rsi
void *v12; // rax
void *v13; // r12
__int64 *v14; // r13
__int64 v15; // rax
void *v16; // r15
void *v17; // rsp
void *v18; // rsp
char v19; // r14
__int64 v20; // rax
__int64 v21; // rax
char *v22; // rax
char *v23; // rbx
char v24; // al
void *v25; // rax
__int64 v26; // r14
int v27; // er9
__int64 v29; // rbx
__int64 v30; // r14
__int64 v31; // rax
__int64 v32; // [rsp+0h] [rbp-160h]
__int64 v33; // [rsp+8h] [rbp-158h]
__int64 *v34; // [rsp+10h] [rbp-150h]
const char *v35; // [rsp+18h] [rbp-148h]
void *v36; // [rsp+20h] [rbp-140h]
void *(**v37)(void *, const char *, ...); // [rsp+28h] [rbp-138h]
int v38; // [rsp+30h] [rbp-130h]
__int64 v39; // [rsp+38h] [rbp-128h]
int v40; // [rsp+40h] [rbp-120h]
int v41; // [rsp+48h] [rbp-118h]
__int64 v42; // [rsp+50h] [rbp-110h]
int v43; // [rsp+58h] [rbp-108h]
int v44; // [rsp+60h] [rbp-100h]
__int64 v45; // [rsp+68h] [rbp-F8h]
int v46; // [rsp+70h] [rbp-F0h]
int v47; // [rsp+78h] [rbp-E8h]
__int64 v48; // [rsp+80h] [rbp-E0h]
int v49; // [rsp+88h] [rbp-D8h]
int v50; // [rsp+90h] [rbp-D0h]
__int64 v51; // [rsp+98h] [rbp-C8h]
__int64 v52; // [rsp+130h] [rbp-30h]
_R15 = (void *)a1;
v3 = 0LL;
if ( a2 )
v4 = &_objc_msgSend_stret;
else
v4 = &_objc_msgSend;
LOBYTE(v3) = a2 != 0;
v5 = *(void **)(a1 + 8 * v3);
v36 = *(void **)(a1 + 8 * v3 + 8);
v6 = (const char *)(8 * v3);
if ( (unsigned __int8)v5 & 1 )
{
v7 = (((unsigned int)v5 ^ objc_debug_taggedpointer_obfuscator) >> 1) & 7;
if ( v7 == 7 )
LOWORD(v7) = (unsigned __int8)(((unsigned int)v5 ^ objc_debug_taggedpointer_obfuscator) >> 4) + 8;
if ( !(_WORD)v7 )
{
LABEL_40:
if ( __e48aedf37b9edb179d065231b52a648b & 0x10 )
___forwarding____cold_4();
v26 = getAtomTarget(v5);
*(_QWORD *)&v6[a1] = v26;
__invoking___(
(__int64)v4,
a1,
a1,
1024,
0LL,
v27,
v32,
v33,
(_DWORD)v34,
(_DWORD)v35,
(__int64)v36,
(_DWORD)v37,
v38,
v39,
v40,
v41,
v42,
v43,
v44,
v45,
v46,
v47,
v48,
v49,
v50,
v51);
if ( *(_QWORD *)a1 == v26 )
*(_QWORD *)a1 = v5;
goto LABEL_43;
}
}
v35 = v6;
v37 = v4;
v33 = a2;
v8 = object_getClass(v5);
v6 = (const char *)class_getName(v8);
if ( (unsigned __int8)class_respondsToSelector(v8, (__int64)"forwardingTargetForSelector:") )
{
v9 = _objc_msgSend(v5, "forwardingTargetForSelector:", v36);
if ( v9 )
{
if ( v9 != v5 )
{
v4 = v37;
v6 = v35;
if ( !((unsigned __int8)v9 & 1) )
goto LABEL_60;
v10 = (((unsigned int)v9 ^ objc_debug_taggedpointer_obfuscator) >> 1) & 7;
if ( v10 == 7 )
LOWORD(v10) = (unsigned __int8)(((unsigned int)v9 ^ objc_debug_taggedpointer_obfuscator) >> 4) + 8;
if ( (_WORD)v10 )
{
LABEL_60:
*(_QWORD *)&v35[a1] = v9;
_R15 = 0LL;
goto LABEL_43;
}
v5 = v9;
goto LABEL_40;
}
}
}
v37 = (void *(**)(void *, const char *, ...))v5;
if ( !strncmp(v6, "_NSZombie_", 0xAuLL) )
goto LABEL_45;
v35 = (const char *)a1;
if ( !(unsigned __int8)class_respondsToSelector(v8, (__int64)"methodSignatureForSelector:") )
{
v29 = class_getSuperclass(v8);
object_getClassName(v37);
if ( v29 )
{
v11 = CFSTR("*** NSForwarding: warning: object %p of class '%s' does not implement methodSignatureForSelector: -- trouble ahead");
}
else
{
object_getClassName(v37);
v11 = CFSTR("*** NSForwarding: warning: object %p of class '%s' does not implement methodSignatureForSelector: -- did you forget to declare the superclass of '%s'?");
}
CFLog(4);
LABEL_50:
v30 = sel_getName(v36, v11);
if ( (void *)sel_getUid(v30) != v36 )
CFLog(4);
v31 = object_getClass(v37);
if ( !(unsigned __int8)class_respondsToSelector(v31, (__int64)"doesNotRecognizeSelector:") )
___forwarding____cold_2(v37);
_objc_msgSend(v37, "doesNotRecognizeSelector:", v36);
BUG();
}
v11 = (const __CFString *)"methodSignatureForSelector:";
v12 = _objc_msgSend(v37, "methodSignatureForSelector:", v36);
if ( !v12 )
goto LABEL_50;
v13 = v12;
v14 = (__int64 *)_objc_msgSend(v12, "_frameDescriptor");
if ( (((unsigned int)*(unsigned __int16 *)(*v14 + 34) >> 6) & 1) != v33 )
{
sel_getName(v36, "_frameDescriptor");
*(_WORD *)(*v14 + 34);
CFLog(4);
}
v15 = object_getClass(v37);
v34 = v14;
if ( (unsigned __int8)class_respondsToSelector(v15, (__int64)"_forwardStackInvocation:") )
{
if ( ___forwarding____onceToken != -1 )
dispatch_once(&___forwarding____onceToken, &__block_literal_global_43);
v16 = _objc_msgSend(&OBJC_CLASS___NSInvocation, "requiredStackSizeForSignature:", v13);
v17 = alloca(__chkstk_darwin(&OBJC_CLASS___NSInvocation));
v6 = (const char *)&v32;
__bzero(&v32);
v18 = alloca(__chkstk_darwin(&v32));
objc_constructInstance(___forwarding____invClass, &v32);
v36 = v16;
_objc_msgSend(&v32, "_initWithMethodSignature:frame:buffer:size:", v13, v35, &v32, v16);
_objc_msgSend(v37, "_forwardStackInvocation:", &v32);
v19 = 1;
}
else
{
v20 = object_getClass(v37);
if ( !(unsigned __int8)class_respondsToSelector(v20, (__int64)"forwardInvocation:") )
___forwarding____cold_3((char *)&v38, (__int64)v37);
v6 = (const char *)_objc_msgSend(&OBJC_CLASS___NSInvocation, "_invocationWithMethodSignature:frame:", v13, v35);
_objc_msgSend(v37, "forwardInvocation:", v6);
v36 = 0LL;
v19 = 0;
}
if ( v6[52] && *(_BYTE *)(*v34 + 34) < 0 )
{
v21 = *v34;
memmove(
*(void **)&v35[*(unsigned int *)(v21 + 28) + *(unsigned __int8 *)(v21 + 32)],
*(const void **)(*(unsigned __int8 *)(v21 + 32) + *((_QWORD *)v6 + 1) + *(unsigned int *)(v21 + 28)),
*(unsigned int *)(*(_QWORD *)v21 + 16LL));
}
v22 = (char *)_objc_msgSend(v13, "methodReturnType");
v23 = v22;
v24 = *v22;
if ( v24 != 118 && (v24 != 86 || v23[1] != 118) )
{
_R15 = (void *)*((_QWORD *)v6 + 2);
if ( v19 )
{
v25 = _objc_msgSend(&OBJC_CLASS___NSData, "dataWithBytes:length:", *((_QWORD *)v6 + 2), v36);
_R15 = _objc_msgSend(v25, "bytes");
_objc_release(v6);
v24 = *v23;
}
if ( v24 == 68 )
__asm { fld tbyte ptr [r15] }
}
else
{
_R15 = &___forwarding____placeholder;
if ( v19 )
_objc_release(v6);
}
LABEL_43:
if ( __stack_chk_guard != v52 )
LABEL_45:
___forwarding____cold_1(v37, v6, v36);
return _R15;
}