Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

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

objective c - Weak property not zeroing using ARC

I have the following simple code for an object that holds a weak reference:

// interface

@interface GMWeakRefObj : NSObject
@property (weak) id object;
@end

// implementation

@implementation GMWeakRefObj 
@synthesize object;
@end

When I run the following test code it fails on the second assert:

NSData* d = [[NSData alloc] init];
GMWeakRefObj* weakRef = [[GMWeakRefObj alloc] init];
weakRef.object = d;
NSAssert(weakRef.object != nil, @"Reference wasn't assigned");
d = nil;
NSAssert(weakRef.object == nil, @"Reference wasn't zeroed"); // <-- FAIL

Aren't ARC weak references supposed to be zeroing? And if so what am I doing wrong?

See Question&Answers more detail:os

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

1 Answer

0 votes
by (71.8m points)

Try some custom class class instead of NSData for d, e.g. MyData. Implement dealloc method in it and set breakpoint in it. You will see, that dealloc is called by autorelease pool after the last NSAssert. Only after that week reference will become nil.

ADD: Looks like I have to extend my answer to make it clear, why it works that way. First, lets look at your example (from comments):

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

It works as expected, weakRef become nil after data = nil. The next example works too:

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
NSLog(@"%@", data);
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

But the last example doesn't work:

NSData* data = [[NSData alloc] init];
__weak NSData* weakRef = data;
NSLog(@"%@", weakRef);
data = nil;
NSAssert(weakRef == nil, @"Failed to zero");

The only difference is that we use weak reference to output log. Why?

(the rest of the answer can be wrong :) )

Imaging that you NSLog (or any other function/selector we call before data = nil) rely on it's argument not to be nil. For example, it has "if (arg == nil) return;" at the very beginning.

In multithreaded environment weak reference can become nil after if.

So properly written function should look like:

  // ...
  T* local_arg = arg;   // NOTE: it is strong!
  if (local_arg == nil)
    return;
  // work with local_arg here, not with arg
  // ...

But usually we don't want to do it everywhere -- it will be ugly. So we want to be sure that arguments will not disappear somewhere in the middle. Compiler does it for us by autoreleasing weak reference.

So, it should be clear how, why your GMWeakRefObj test case doesn't work -- weakRef is autoreleased before calling setObject setter.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...