美文网首页
ARC技术的本质

ARC技术的本质

作者: 平凡码农 | 来源:发表于2019-02-14 18:04 被阅读28次

    在ARC下,我们不需要手动去retain/release以及autorelease,网上有很多教程说是llvm在编译期自动帮我们插入了这些操作。

    抱着怀疑的态度,我们来研究下是否真的如此?

    int main(int argc, char * argv[]) {
        @autoreleasepool {
    
            NSObject *obj = [[NSObject alloc] init];
    
            NSObject *strongObj = obj;
            __weak NSObject *weakObj = obj;
        }
    }
    

    使用clang来对OC代码编译成C++

    clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.m 
    

    编译为C++后

    int main(int argc, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
    
            NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    
            NSObject *strongObj = obj;
            __attribute__((objc_ownership(weak))) NSObject *weakObj = obj;
        }
    }
    

    这里出现了__attribute__((objc_ownership(weak))),这是什么鬼?

    插播一些__attribute__的知识

    attribute是一个编译指令,可以指定在声明时的错误检查及高级优化的特性。
    此处的objc_ownership(weak)就是告诉编译器weakObj的内存引用类型为weak。

    可是这又如何说明插入了retain/release/autorelease呢?

    继续深入,把OC直接编译成汇编呢?

    clang -fobjc-runtime=ios-8.0.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk -S main.m
    

    OK,现在生成了main.s文件

        .section    __TEXT,__text,regular,pure_instructions
        .build_version ios, 12, 1
        .globl  _main                   ## -- Begin function main
        .p2align    4, 0x90
    _main:                                  ## @main
        .cfi_startproc
    ## %bb.0:
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset %rbp, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register %rbp
        subq    $64, %rsp
        movl    %edi, -4(%rbp)
        movq    %rsi, -16(%rbp)
        callq   _objc_autoreleasePoolPush
        movq    L_OBJC_CLASSLIST_REFERENCES_$_(%rip), %rsi
        movq    L_OBJC_SELECTOR_REFERENCES_(%rip), %rcx
        movq    %rsi, %rdi
        movq    %rcx, %rsi
        movq    %rax, -48(%rbp)         ## 8-byte Spill
        callq   *_objc_msgSend@GOTPCREL(%rip)
        movq    L_OBJC_SELECTOR_REFERENCES_.2(%rip), %rsi
        movq    %rax, %rdi
        callq   *_objc_msgSend@GOTPCREL(%rip)
        movq    %rax, -24(%rbp)
        movq    -24(%rbp), %rax
        movq    %rax, %rdi
        callq   *_objc_retain@GOTPCREL(%rip)
        leaq    -40(%rbp), %rcx
        movq    %rax, -32(%rbp)
        movq    -24(%rbp), %rax
        movq    %rcx, %rdi
        movq    %rax, %rsi
        callq   _objc_initWeak
        leaq    -40(%rbp), %rcx
        movq    %rcx, %rdi
        movq    %rax, -56(%rbp)         ## 8-byte Spill
        callq   _objc_destroyWeak
        xorl    %edx, %edx
        movl    %edx, %esi
        leaq    -32(%rbp), %rax
        movq    %rax, %rdi
        callq   _objc_storeStrong
        xorl    %edx, %edx
        movl    %edx, %esi
        leaq    -24(%rbp), %rax
        movq    %rax, %rdi
        callq   _objc_storeStrong
        movq    -48(%rbp), %rdi         ## 8-byte Reload
        callq   _objc_autoreleasePoolPop
        xorl    %eax, %eax
        addq    $64, %rsp
        popq    %rbp
        retq
        .cfi_endproc
                                            ## -- End function
        .section    __DATA,__objc_classrefs,regular,no_dead_strip
        .p2align    3               ## @"OBJC_CLASSLIST_REFERENCES_$_"
    L_OBJC_CLASSLIST_REFERENCES_$_:
        .quad   _OBJC_CLASS_$_NSObject
    
        .section    __TEXT,__objc_methname,cstring_literals
    L_OBJC_METH_VAR_NAME_:                  ## @OBJC_METH_VAR_NAME_
        .asciz  "alloc"
    
        .section    __DATA,__objc_selrefs,literal_pointers,no_dead_strip
        .p2align    3               ## @OBJC_SELECTOR_REFERENCES_
    L_OBJC_SELECTOR_REFERENCES_:
        .quad   L_OBJC_METH_VAR_NAME_
    
        .section    __TEXT,__objc_methname,cstring_literals
    L_OBJC_METH_VAR_NAME_.1:                ## @OBJC_METH_VAR_NAME_.1
        .asciz  "init"
    
        .section    __DATA,__objc_selrefs,literal_pointers,no_dead_strip
        .p2align    3               ## @OBJC_SELECTOR_REFERENCES_.2
    L_OBJC_SELECTOR_REFERENCES_.2:
        .quad   L_OBJC_METH_VAR_NAME_.1
    
        .section    __DATA,__objc_imageinfo,regular,no_dead_strip
    L_OBJC_IMAGE_INFO:
        .long   0
        .long   96
    
    
    .subsections_via_symbols
    

    首先看到了

    _objc_autoreleasePoolPush
    _objc_autoreleasePoolPop
    

    这是自动释放池的内容,我们先留着不管。

    继续往下挑代码

        callq   *_objc_retain@GOTPCREL(%rip)
        callq   _objc_initWeak
        callq   _objc_destroyWeak
        callq   _objc_storeStrong
        callq   _objc_storeStrong
    

    汇编看不懂,我们来猜测一下如何?

    objc_retain是对对象进行retain操作,说明NSObject *obj = [[NSObject alloc] init];时,执行了retain操作。

    objc_initWeak是对对象进行弱引用,objc_destroyWeak是对weak指针做释放。

    objc_storeStrong是对obj赋值时调用,对新值retain,对旧值release。其实这里是编译器为我们在尾端加入了obj = nil;strongObj = nil;的操作。赋值为nil就相当于做release操作。

    这就是传说中的,实际上自动插入了retain/release!!!

    小伙伴们可能对objc_retain_objc_initWeak、objc_destroyWeak、objc_storeStrong这些个函数是什么,又具体做了什么操作比较感兴趣,其实这里面就是引用计数及弱引用的底层实现。还有AutoreleasePool中_objc_autoreleasePoolPush、_objc_autoreleasePoolPop的的东西暂时先不表了,放到后续文章来分析。

    相关文章

      网友评论

          本文标题:ARC技术的本质

          本文链接:https://www.haomeiwen.com/subject/qgfyeqtx.html