3

So I was reading How do I avoid capturing self in blocks when implementing an API? about how memory management works in regards to referencing self within a completion block and that got me thinking: will the following cause a retain cycle?

Pseudocode:

[AFnetworking requestGet:@"http://www.website.com" completionBlock:(^)(RequestObj* request, NSError* error){
      [self.tableView reloadData];
}];

Ignoring syntax problems, does self.tableView cause a retain cycle? Is it necessary to do the following instead?

__weak id weakSelf = self;
[AFnetworking requestGet:@"http://www.website.com" completionBlock:(^)(RequestObj* request, NSError* error){
      [weakSelf.tableView reloadData];
}];

Or is there some sort of memory magic that AFNetworking does to prevent this?


Edit courtesy of Aaron Brager

Here you don't have a retain cycle. But if you did, in the completion block, you should convert weakSelf back into a strong reference so it won't get deallocated halfway through your completion block.

id strongSelf = weakSelf; 
[strongSelf.tableView reloadData];
Community
  • 1
  • 1
Endama
  • 743
  • 8
  • 25
  • 1
    Here you don't have a retain cycle. But if you did, in the completion block, you should convert `weakSelf` back into a strong reference so it won't get deallocated halfway through your completion block. (`id strongSelf = weakSelf; [strongSelf.tableView reloadData];`). – Aaron Brager Oct 09 '13 at 19:31

1 Answers1

6

A retain cycle occurs when two or more objects have strong references to each other.

In this case, the block will have a strong reference to self, but self doesn't have a strong reference to the block, so you are fine here and no need to use weakSelf.

The case when you'll have a retain cycle and need to break it by using the weakSelf is when the class has a strong reference to the block too like in the following example:

typedef void (^CompletionCallback)(RequestObj* request, NSError* error);

@interface SomeClass() {
    /// Strong reference to the block
    CompletionCallback completionBlock;
}
@end

@implementation SomeClass()

- (void)someMethod {
    completionBlock = ^(RequestObj* request, NSError* error) {
        /// Strong reference to the class
        [self.tableView reloadData];
    };

    [AFnetworking requestGet:@"http://www.website.com" completionBlock:completionBlock];
}

@end

In this example, both completionBlock and self have strong references to each other, and therefore you'll have a retain cycle here and need to break it.

Hejazi
  • 16,587
  • 9
  • 52
  • 67
  • 1
    It's people like you, Hejazi, that make me love stackoverflow. Thank you for being awesome. – Endama Oct 09 '13 at 19:27
  • Glad to be helpful. I updated the answer to give an example where you'll have a retain cycle. – Hejazi Oct 09 '13 at 19:47
  • 3
    Keep in mind the strong reference cycle may not be this straightforward. For example, if your controller has a strong reference to an object, that object has a strong reference to a completion block, and then the completion block strongly references your controller, you'll still have a retain cycle, just with 3 objects instead of 2. – Aaron Brager Oct 09 '13 at 20:35
  • Yes, that's why I said "when two or *more objects* have strong references to each other", but in our case the controller doesn't have a strong reference to an instance of `AFnetworking` for example. – Hejazi Oct 09 '13 at 20:39