#方法决议和方法转发

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寻找一遍。
具体流程图如下:

resolveMethod_locked 流程分析.png

疑问:元类方法 调用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方法后对forwardingTargetForSelectormethodSignatureForSelector方法进行了调用。
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___.png

__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;
}

流程图梳理

__forwarding__流程分析.png

你可能感兴趣的:(#方法决议和方法转发)