It is well known that when referring self or instance variable inside a block, it is better to use
a __weak reference variable to avoid memory leak due to retain cycle. But this may not be necessary in all the cases, and sometimes it is better to use self instead a weak self reference.
First let see a case how retain cycle happens.
@interface RetainCycle : NSObject{
void (^testBlock)(void);
}
@property (strong) NSString* mystr;
-(void)setup;
@end
@implementation RetainCycle
-(void)dealloc{
NSLog(@"RetainCycle instance: %@ deallocated", self);
}
-(id)init {
NSLog(@"Init called %@", self);
return self;
}
-(void)setup{
testBlock= ^{
self.mystr = @"hi";
NSLog(@"Integer is: %@", self.mystr);
};
}
@end
For the above RetainCycle interface, if an app calls the below method
-(void) test {
RetainCycle* rc = [[RetainCycle alloc] init];
NSLog(@"RetainCycle obj: %@ created", rc);
[rc setup];
}
then after the method returns, the RetainCycle instance will not be released. The reason is self is retained within the testBlock due to reference to self.mystr, and testBlock is retained as a property of RetainCycle.
The means retain cycle only happens, if the block is keeping retained by self or its property.
If we change the above code to the below line, then the retain cycle related memory leak will not happen
__weak void (^testBlock)(void);
Now, we can look another popular case, where retain cycle does not happen, that is the completion block used by view controller.
@interface TestViewController : UIViewController
@end
@implementation TestViewController
-(void)dealloc{
NSLog(@"*******TestViewController instance: %@ deallocated", self);
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (IBAction)onDismiss:(id)sender {
[self.presentingViewController dismissViewControllerAnimated:YES completion:^{
NSLog(@"Test View controller dismissed %@", self);
}];
}
@end
In this case, after creating and showing the viewcontroller, user can click the dismiss button to dismiss the view controller. Notice the dismissViewController method's completion block also refers self for NSLog output, but it would not cause memory leak due to retain cycle.
The reason is although the block retains the self for writing NSLog data. But self (viewController) only temporarily retain the block. After the viewController is dismissed, the completion block returned by view controller will be released, and so block will deallocate itself and all objects retained by itself. So there is no need to change self to a weak reference in the completion block used by view controller. This is also indicated by the xcode debugger, which will call _Block_release after the view controller is dismissed
Actually in most case, we may want to use strong self for view controller completion block, as the operation will need the view controller alive to finish, such as read the text input from the view controller's text field.