简体   繁体   中英

What is a simple rule to avoid objective-c block retain cycle memory leaks?

I've had it with memory leaks arising from block retain cycles. I would just like a simple rule that I can apply to my code to make sure I avoid them. On the other hand, I don't want to update half of my code base base to __weak pointers without it being necessary.

Here is what I have up to now:

There can be no memory leaks when you use the following:

dispatch_async(queue, ^{...}); // GCD call.
[Foo bar:^{...}]; // Class "+" methods with completion block.

However, these cases will cause block retain cycle memory leaks for sure:

self.myPropertyBlock = ^{ self; };
_myInstanceVariableBlock = ^{ self; };
self.myPropertyBlock = ^{ _selfInstanceVariable; };
obj.myPropertyBlock = ^{ obj; };

And these cases might or might not cause block retain cycle memory leaks (depending on if the block calling object retains the block):

[self bar:^{ self; }];    
[self.myPropertyObj bar:^{ self; }];
[self.myPropertyObj bar:^{ _selfInstanceVariable; }];

[obj bar:^{ obj; }];
[obj.myPropertyObj bar:^{ obj; }];

To be absolutely sure there can be no memory leaks, the problematic cases require to change the self or obj pointers used inside the blocks to __weak pointers like this (or some other leak avoiding strategy):

__weak SelfClass *weakSelf = self;
self.myPropertyBlock = ^{ weakSelf; };
_myInstanceVariableBlock = ^{ weakSelf; };
self.myPropertyBlock = ^{ weakSelf.instanceVariableConvertedToProperty; };
[self bar:^{ weakSelf; }];
[self.myPropertyObj bar:^{ weakSelf; }];
[self.myPropertyObj bar:^{ weakSelf.instanceVariableConvertedToProperty; }];

__weak ObjClass *weakObj = obj
[obj bar:^{ weakObj; }];
[obj.myPropertyObj bar:^{ weakObj; }];
obj.myPropertyBlock = ^{ weakObj; };

Please alert me about any wrong cases above.

Are there simpler and better rules?

It would be awesome if you could add a little explanation explaining why a rule works or doesn't.

Rule based on answer: Consider all of the objects mentioned in the block and ask, do any of these objects retain this block?

A retain cycle is when A -> B -> A (where -> means retains). It's bad because we cannot deallocate retained things, so the only way to deallocate A is to deallocate B, but that depends on deallocating A.

A block retain cycle is no different, except blocks are more aggressive about retaining: They retain any object referenced within them, so if A -> Block and Block mentions A, then A -> Block -> A.

All this leads to a simple rule when coding blocks. Consider all of the objects mentioned in the block and ask, do any of these objects retain this block? Most of the time they don't. But pay special attention to the object you're passing the block to, in other words:

[beSuspiciousOfMe heresABlock:^{
    NSLog(@"does %@ retain this block?", beSuspiciousOfMe];
}];

If you control beSuspiciousOfMe (which can be, and often is self), this is easy to check. If that code is opaque for some reason, and you're not sure, you can use the __weak copy trick.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM