adobels adobels - 3 months ago 34
Objective-C Question

__weak self in blocks

I have __weak reference to self. Do I need __weak reference for controller2 and controller 3 which are also referenced in the competition block ?

UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

id controller1 = [sb instantiateViewControllerWithIdentifier:@"controller1"];

id controller2 = [sb instantiateViewControllerWithIdentifier:@"controller2"];

id controller3 = [sb instantiateViewControllerWithIdentifier:@"controller3"];

__weak typeof(self) weakSelf = self;

[self presentViewController:controller1 animated:YES completion:^{

[(UINavigationController *)weakSelf.parentViewController setViewControllers:@[controller2, controller3] animated:NO];

}];


EDIT what about the following code? Does the block in the following code require a weak reference to self?

typedef void(^MyCustomBlock)(void);

@property (strong, readwrite, nonatomic) MyCustomBlock customBlock;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
__weak typeof(self) weakSelf = self;
self.customBlock = ^{
[(UINavigationController *)weakSelf.parentViewController setViewControllers:@[controller2, controller3] animated:NO];
};
[self presentViewController:controller1 animated:YES completion:self.customBlock];
}

Answer

Its harmless (in this case) to declare and use weak copies, but none of the block's references need to be weak.

The question is a symptom of a common misunderstanding: All objects to which a block refers are maintained as strong references (retained) by the block. If any those objects in turn keep a strong reference to the block, then you have a retain cycle.

Just like it sounds, a retain cycle is when objects strongly refer to each other, either directly:

block ---> objectA ---> block  ("--->" means retains)

... or indirectly:

block ---> objectA ---> objectB ---> block

Retain cycles are bad because the system won't free objects that are retained by other objects. So when we try to free the block, we can't because (in the direct case) it's retained by objectA, and we can't free objectA because it's retained by the block.

The simple fix is to declare a weak copy of the pointer, telling the block in essence "Don't retain this object. I promise it will outlive the block".

Looking at the OP code, the block passed to presentViewController isn't retained at all. It's held long enough to do the presentation, invoked when the animation is complete and then discarded. Since none of the objects referred to by the block retain a copy of that block, there's no risk for a retain cycle, and no need for any weak references at all.

// initialize controller1, 2, 3

// no need for this
//__weak typeof(self) weakSelf = self;

[self presentViewController:controller1 animated:YES completion:^{
    // perfectly safe...
    [(UINavigationController *)self.parentViewController setViewControllers:@[controller2, controller3] animated:NO];
}];

EDIT Regarding the additional code in the edit: Yes, in that case, since you retain the block and the block refers to self, a weak copy must be used in order to avoid the retain cycle.