I noticed the following in Objective-C with ARC enabled:
Let's have simple class A and autosynthesized weak property
@interface A
@property (nonatomic, weak) id refObject;
@end
@implementation A
@end
And second class B with dealloc implemented
@interface B
@end
@implementation B
-(void) dealloc
{
NSLog(@"In dealloc");
}
@end
And finally somewhere in class A have the following:
@implementation A
...
-(void) foo
{
B* b = [B new];
self.refObject = b;
// Just use b after the weak assignment
// in order to not dealloc 'b' before assignement
NSLog(@"%@", b);
}
...
@end
If I set a breakpoint in [B dealloc]
and inspect [A refObject]
property I can see that a.refObject
is nil but a->_refObject
is not nil and points to 'b'
Any ideas why that happens?
Short answer: The instance variable
a->_refObject
is not (yet) nil in-[B dealloc]
, but each access to that weak pointer is done through a ARC runtime function that returns nil if the deallocation has already begun.Long answer: By setting a watchpoint you can see that
a->_refObject
is set to nil at the end of the deallocation process. The stack backtrace (when the watchpoint is hit) looks like this:and
object_dispose()
is called from-[NSObject dealloc]
(as can be seen in http://www.opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm).Therefore in
-[B dealloc]
,a->_refObject
is not nil before the (compiler generated)[super dealloc]
is called.So the question remains: Why does
a.refObject
return nil at that point?The reason is that for each access to a weak pointer the ARC compiler generates a call to
objc_loadWeak()
orobjc_loadWeakRetained()
. From the documentation:So even if
a->refObject
is not nil at that point, accessing the weak pointer viaobjc_loadWeakRetained()
(as done by the property accessor method) returns nil, because the deallocation of theB
object has already begun.The debugger accesses
a->refObject
directly and does not callobjc_loadWeak()
.