初探OC底层原理之objc_msgSend(上)

了解编译时和运行时

编译时

  • 编译时顾名思义就是正在编译的时候.那啥叫编译呢?就是编译器帮你把源代码翻译成机器能识别的代码.(当然只是一般意义上这么说,实际上可能只是翻译成某个中间状态的语言.比如Java只有JVM识别的字节码,C#中只有CLR能识别的MSIL.另外还有啥链接器.汇编器.为了了便于理解我们可以统称为编译器) 那编译时就是简单的作一些翻译工作,比如检查老兄你有没有粗心写错啥关键字了啊.有啥词法分析,语法分析之类的过程.就像个老师检查学生的作文中有没有错别字和病句一样.如果发现啥错误编译器就告诉你.如果你用微软的VS的话,点下build.那就开始编译,如果下面有errors或者warning信息,那都是编译器检查出来的.所谓这时的错误就叫编译时错误,这个过程中做的啥类型检查也就叫编译时类型检查,或静态类型检查(所谓静态嘛就是没把真把代码放内存中运行起来,而只是把代码当作文本来扫描下).所以有时一些人说编译时还分配内存啥的肯定是错误的说法

运行时

  • 所谓运行时就是代码跑起来了.被装载到内存中去了.(你的代码保存在磁盘上没装入内存之前是个死家伙.只有跑到内存中才变成活的).而运行时类型检查就与前面讲的编译时类型检查(或者静态类型检查)不一样.不是简单的扫描代码.而是在内存中做些操作,做些判断.

分析objc_msgSend

  • oc有3种形态 1. objective-c code([person sayNB]),2Framework&Serivce(isKindofClass),3.Runtime API (class_getInstanceSize)


    image.png
@interface LGTeacher : NSObject
- (void)sayHello;
@end

@implementation LGTeacher
- (void)sayHello{
    NSLog(@"666 %s",__func__);
}
@end

@interface LGPerson : LGTeacher
- (void)sayHello;
- (void)sayNB;
@end

@implementation LGPerson
- (void)sayNB{
    NSLog(@"666");
}
@end

int main(int argc, const char * argv[]) {
@autoreleasepool {

    
LGPerson *person = [LGPerson alloc];

[person sayNB];

// 子类是不是没有 -> 父类
[person sayHello];
  • main.m clang 编译点成c++如下
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_hd_1nh9wr41349b07x6594n5k9r0000gn_T_main_f442e1_mi_2);
    }
    return 0;
}
  • 从上可以分享出方法调用底层就是 objc_msgSend 发消息

分析objc_msgSend底层汇编

    ENTRY _objc_msgSend //汇编常用方法进入ENTRY
    UNWIND _objc_msgSend, NoFrame

    cmp p0, #0          // 寄存器p0是objc_msgSend的第一个参数`id self`,和0比较判断有没有消息接收者
    #if SUPPORT_TAGGED_POINTERS //判断条件是否支持 tagged pointer
    b.le    LNilOrTagged        //  执行LNilOrTagged开始向下执行#else
    b.eq    LReturnZero  // 不存在直接返回Zero
#endif
    ldr p13, [x0]       // p13 = isa x0寄存器为消息接收者,将x0赋值给p13,p13 = 接收者的isa
    GetClassFromIsa_p16 p13, 1, x0 //调用GetClassFromIsa_p16方法    // p16 = class
LGetIsaDone:
    // calls imp or objc_msgSend_uncached
    CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached

#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
    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
.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__
.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
.macro TailCallCachedImp
    // $0 = cached imp, $1 = address of cached imp, $2 = SEL, $3 = isa
    eor $1, $1, $2  // mix SEL into ptrauth modifier
    eor $1, $1, $3  // mix isa into ptrauth modifier
    brab    $0, $1
.endmacro

.macro ExtractISA
    and    $0, $1, #ISA_MASK
.endmacro
  • 0=p16,正如前面文章提到 isa & isa mask 得到class 的验证

你可能感兴趣的:(初探OC底层原理之objc_msgSend(上))