我正在阅读ARC的一些深层实现, 通过读取xcode生成的汇编代码,我们可以看到编译器插入了像objc_release,objc_storeStrong这样的代码。
如果我们有一个代码:
-(void)foo
{
NSDate * _date = [[NSDate alloc] init];
}
汇编代码如下:
movq %rdi, -32(%rbp) ## 8-byte Spill
movq %rsi, %rdi
movq -32(%rbp), %rsi ## 8-byte Reload
callq _objc_msgSend (this is call of 'alloc')
movq L_OBJC_SELECTOR_REFERENCES_2(%rip), %rsi
movq %rax, %rdi
callq _objc_msgSend (this is call of 'init')
movq %rax, -24(%rbp)
.loc 1 30 0
在{}结束时,编译器插入_objc_storeStrong
movq %rdx, %rdi
callq _objc_storeStrong (in this _objc_storeStrong, _date is released)
addq $64, %rsp
popq %rbp
retq
编译_objc_storeStrong(_date,nil); cos没有人持有Date变量。
这是有道理的
然后我这样写:date成为一个强大的成员变量
@interface MyView : UIView
@property(nonatomic,strong) NSDate * date;
@end
-(void)foo
{
self.date = [[NSDate alloc] init];
}
汇编代码是:
movq %rax, -48(%rbp) ## 8-byte Spill
callq _objc_msgSend (call alloc)
movq L_OBJC_SELECTOR_REFERENCES_2(%rip), %rsi
movq %rax, %rdi
callq _objc_msgSend (call init)
movq L_OBJC_SELECTOR_REFERENCES_4(%rip), %rsi
movq -48(%rbp), %rdi ## 8-byte Reload
movq %rax, %rdx
movq %rax, -56(%rbp) ## 8-byte Spill
callq _objc_msgSend (call setDate:)
movq -56(%rbp), %rax ## 8-byte Reload
movq %rax, %rdi
callq _objc_release ( a magic release,~!!!)
基本上,setDate将保留日期,最后一行将释放它以获得余额。(不是不是,请参见下面的测试)
然后我将代码更改为
@interface MyView : UIView
@property(nonatomic,strong) NSDate * date;
@end
-(void)foo
{
_date = [[NSDate alloc] init];
}
汇编代码变为
callq _objc_msgSend (call alloc)
movq L_OBJC_SELECTOR_REFERENCES_2(%rip), %rsi
movq %rax, %rdi
callq _objc_msgSend (call init)
movq -8(%rbp), %rsi
movq _OBJC_IVAR_$_MyView._date(%rip), %rdi (set date directly)
movq (%rsi,%rdi), %rcx
movq %rax, (%rsi,%rdi)
movq %rcx, %rdi
callq _objc_release ( a magic release? here? why, we didn't retain it? )
编译器也插入_objc_release,为什么?我们这里没有添加任何保留代码,
这让我困惑了一天,通过测试,_date实际上没有发布,你可以使用它。
我在objc_release上添加了一个符号断点来跟踪保留计数, 如果我们使用self.date = [[NSDate alloc] init],objc_release中断2次, 通过检查保留计数(CFGetRetainCount),retainCount是2然后是1,这是正确的。
如果使用_date = [[NSDate alloc] init],objc_release仅中断一次,则retainCount为1。
有人会帮我理解这个吗?还有一个问题,符号点,我使用lldb上的寄存器读取来读取第一个寄存器内容,并打印保留计数,是否正确?通常r0 r1 r2包含该方法的参数。
答案 0 :(得分:0)
编译器应该释放存储在ARC环境下__strong变量中的旧值。
_date = [[NSDate alloc] init];
在这种情况下,应释放_date变量的值。因此,编译器发出如下代码。
NSDate *newDate = [[NSDate alloc] init];
NSDate *oldDate = _date;
_date = newDate;
[oldDate release];
发出了 callq _objc_release
以释放oldDate值。