iOS如何插入ARC代码?

时间:2014-12-09 08:51:35

标签: ios objective-c automatic-ref-counting

我正在阅读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包含该方法的参数。

1 个答案:

答案 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值。