aryaxt
aryaxt

Reputation: 77646

Objective-C proper use of blocks with ARC?

I know this question has been asked before, but non of the solutions solve my problem, so I'm asking this again. I am trying to call a method on self as the result of a callback through a block. I'm getting the following error:

Capturing 'self' strongly in this block is likely to lead a retain cycle

- (void)viewDidLoad {
   [super viewDidLoad];

   self.webClient.completionHandler = ^{
      [self populateData];
   };
}

I tried doing something like the code below, and I'm still getting the same warning. What's the solution?

__weak id myself = self;
[myself populateData];  

Upvotes: 0

Views: 426

Answers (2)

gnasher729
gnasher729

Reputation: 52632

UIAdam gave the correct answer, but it's worth understanding why it is correct.

First, why did you get the warning?

self has a strong reference to webClient. webClient has a strong reference to completionHandler. completionHandler has a strong reference to self. So if all other references in your program go away, there is still a strong reference to each item in this cycle, so they can never be deallocated.

The attempt of writing

__weak id myself = self;
[myself populateData];  

doesn't work of course. The block still references self because it is assigning it to myself. So no difference here.

UIAdam's solution of writing

__weak id weakSelf = self; self.webClient.completionHandler = ^{ [weakSelf populateData]; };

means that weakSelf is a weak reference, and the block only contains a weak reference to self. So if all other strong references to self is gone, there's only a weak reference left. A weak reference doesn't keep self alive, so self gets deallocated.

Now what if that happens, but something else had a strong reference to webClient and your block is called? weakSelf is a weak reference, and weak references are set to nil when the object is deallocated. So you have to be prepared that weakSelf is nil when your block gets called. It is actually better to write

id strongSelf = weakSelf;
[strongSelf populatedData];

inside the block: strongSelf might be set to nil, or it will be set to self. But since it is a strong reference, it will stay non-nil until the block has finished. If it was not nil to start with.

Upvotes: 0

UIAdam
UIAdam

Reputation: 5313

Your code should look like this:

- (void)viewDidLoad {
   [super viewDidLoad];

   __weak id weakSelf = self;
   self.webClient.completionHandler = ^{
      [weakSelf populateData];
   };
}

Upvotes: 4

Related Questions