在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
的的东西暂时先不表了,放到后续文章来分析。
网友评论