objc_msgSend汇编分析(上)

通过clang将OC代码翻译成C++代码之后,我们发现,OC的方法调用的本质是消息发送的过程

clang -rewrite-objc main.m -o main.cpp

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
        LGTeacher *teach = ((LGTeacher *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGTeacher"), sel_registerName("alloc"));

        ((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayNB"));

        ((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHello"));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_hr_l_56yp8j4y11491njzqx6f880000gn_T_main_c501bc_mi_2);
    }
    return 0;
}

所有的秘密从objc_msgSend开始,下面从底层源码分析一下objc_msgSend都做了什么。首先从上面的clang输出代码可以看出, objc_msgSend调用的第一个参数是消息接收者,第二个参数是SEL。从objc-msg-arm64.s文件中找到了objc_msgSend的汇编源码,下面代码中的中文注释解释了每条重要指令的意义

    ENTRY _objc_msgSend
    UNWIND _objc_msgSend, NoFrame

//  p0 = 消息接收者,这里消息接收者和0比较
    cmp p0, #0          // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
//  如果小于等于0,跳到LNilOrTagged
    b.le    LNilOrTagged        //  (MSB tagged pointer looks negative)
#else
//  如果等于0,跳到LReturnZero,其实就返回0
    b.eq    LReturnZero
#endif
//  类偏移0的位置存储的是isa,isa放到p13
    ldr p13, [x0]       // p13 = isa
    GetClassFromIsa_p16 p13, 1, x0  // p16 = class
LGetIsaDone:
    // calls imp or objc_msgSend_uncached
    CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached

#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
//  如果等于0,返回0
    b.eq    LReturnZero     // nil check
//  这个跳转先放一放,下回分解
    GetTaggedClass
    b   LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif

LReturnZero:
    // x0 is already zero
    mov x1, #0
    movi    d0, #0
    movi    d1, #0
    movi    d2, #0
    movi    d3, #0
    ret

    END_ENTRY _objc_msgSend

下一步跳转到GetClassFromIsa_p16宏
GetClassFromIsa_p16 p13, 1, x0 // p16 = class

.macro GetClassFromIsa_p16 src, needs_auth, auth_address /* note: auth_address is not required if !needs_auth */

#if SUPPORT_INDEXED_ISA    //  不走这里
    // Indexed isa
    mov p16, \src           // optimistically set dst = src
    tbz p16, #ISA_INDEX_IS_NPI_BIT, 1f  // done if not non-pointer isa
    // isa in p16 is indexed
    adrp    x10, _objc_indexed_classes@PAGE
    add x10, x10, _objc_indexed_classes@PAGEOFF
    ubfx    p16, p16, #ISA_INDEX_SHIFT, #ISA_INDEX_BITS  // extract index
    ldr p16, [x10, p16, UXTP #PTRSHIFT] // load class from array
1:

#elif __LP64__
//  传入的needs_auth = 1
.if \needs_auth == 0 // _cache_getImp takes an authed class already
    mov p16, \src
.else
//  直接跳到这里
    // 64-bit packed isa
    ExtractISA p16, \src, \auth_address
.endif
#else
    // 32-bit raw isa
    mov p16, \src

#endif

.endmacro

SUPPORT_INDEXED_ISA = 0,所有我们进入__LP64__的代码段,我们传人的needs_auth = 1,所以直接跳到ExtractISA
ExtractISA p16, \src, \auth_address

// p16 = isa & ISA_MASK
.macro ExtractISA
//  $0 = p16, $1 = isa,isa & ISA_MASK = 类地址
//  p16 = 类地址
    and    $0, $1, #ISA_MASK
.endmacro

GetClassFromIsa_p16其实就是通过isa & ISA_MASK取到类地址,类地址放到p16里

取到类地址之后,跳转到CacheLookup
CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached,这个宏我们在下一编文章中继续分析

未完待续

你可能感兴趣的:(objc_msgSend汇编分析(上))