2
votes

Im reading some deep implementations of ARC, by read the assemble code generated by xcode, we can see the compiler inserts code like, objc_release, objc_storeStrong, things like that.

if we have a code like:

-(void)foo
{
   NSDate * _date = [[NSDate alloc] init]; 
}

the assemble code is like:

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   

and at the end of { }, compiler inserts _objc_storeStrong

movq    %rdx, %rdi
callq   _objc_storeStrong  (in this _objc_storeStrong, _date is released)
addq    $64, %rsp
popq    %rbp
retq

compile does _objc_storeStrong(_date,nil); cos no one strong holds the Date variable.

That makes sense

then I write like this : date becomes a strong member variable

@interface MyView : UIView
@property(nonatomic,strong) NSDate * date;
@end

-(void)foo
{
   self.date = [[NSDate alloc] init]; 
}

the assemble code is :

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,~!!!)

basically, setDate will retain the date, and the last line release it for a balance.(not its not, see below test)

Then I change the code to

@interface MyView : UIView
@property(nonatomic,strong) NSDate * date;
@end

-(void)foo
{
   _date = [[NSDate alloc] init]; 
}

assemble code becomes

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? )

Compiler inserts an _objc_release either, why? we didn't add any retain code here,

this really confused me for a day, by testing it, the _date is not released actually, you could use it then.

I added a symbolic break point on objc_release to trace the retain count, if we use self.date = [[NSDate alloc] init], objc_release breaks 2 times, by checking the retain count (CFGetRetainCount), retainCount is 2 and then 1, which is correct.

if using _date = [[NSDate alloc] init], objc_release breaks only once, the retainCount is 1.

will someone help me to understand this ?

one more question, the symbolic point, I use register read on lldb to read the first register content, and print the retain count, is it correct? generally r0 r1 r2 contains parameters for the method.

1

1 Answers

0
votes

The compiler should release the old value that was stored in the __strong variable under ARC environment.

_date = [[NSDate alloc] init];

In this case, the value of the _date variable should be released. Thus, the compiler emits codes as the following.

NSDate *newDate = [[NSDate alloc] init];
NSDate *oldDate = _date;
_date = newDate;
[oldDate release];

callq _objc_release was emitted for releasing the oldDate value.