Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
606 views
in Technique[技术] by (71.8m points)

objective c - Weak property is set to nil in dealloc but property's ivar is not nil

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:

frame #0: 0x00007fff8ab9f0f8 libobjc.A.dylib`arr_clear_deallocating + 83
frame #1: 0x00007fff8ab889ee libobjc.A.dylib`objc_clear_deallocating + 151
frame #2: 0x00007fff8ab88940 libobjc.A.dylib`objc_destructInstance + 121
frame #3: 0x00007fff8ab88fa0 libobjc.A.dylib`object_dispose + 22
frame #4: 0x0000000100000b27 weakdealloc`-[B dealloc](self=0x000000010010a640, _cmd=0x00007fff887f807b) + 151 at main.m:28
frame #5: 0x0000000100000bbc weakdealloc`-[A foo](self=0x0000000100108290, _cmd=0x0000000100000e6f) + 140 at main.m:41
frame #6: 0x0000000100000cf5 weakdealloc`main(argc=1, argv=0x00007fff5fbff968) + 117 at main.m:52
frame #7: 0x00007fff8c0987e1 libdyld.dylib`start + 1

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() or objc_loadWeakRetained(). From the documentation:

id objc_loadWeakRetained(id *object)

If object is registered as a __weak object, and the last value stored into object has not > yet been deallocated or begun deallocation, retains that value and returns it. Otherwise > returns null.

So even if a->refObject is not nil at that point, accessing the weak pointer via objc_loadWeakRetained() (as done by the property accessor method) returns nil, because the deallocation of the B object has already begun.

The debugger accesses a->refObject directly and does not call objc_loadWeak().


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...