简体   繁体   中英

ARC __block and __weak

Let's say I'm trying to access self from within a block:

[someObject successBlock:^(NSArray *result) {
    [self someSuccessMethod];
} failure:^(NSString *errorMessage, int status) {
    [self someFailureMethod];
}];

I understand that this creates a retain cycle and that someObject and self never get de-alloced.

What's confusing me is what actually happens with/without the __block keyword. I can fix the retain cycle by making a __weak reference to self:

__weak MyClass* me = self;
[someObject successBlock:^(NSArray *result) {
    [me someSuccessMethod];
} failure:^(NSString *errorMessage, int status) {
    [me someFailureMethod];
}];

I don't need to use __block here, because I'm not trying to modify me from within the block. From what I understand, if I don't use __block , a copy of me is referenced inside the block. My question is: if what's being referenced inside the block is just a copy of the object, why does the original code block create the retain cycle? I would guess that the reference to self is just a copy, since I'm never using the __block keyword. Am I thinking about this incorrectly?

In the first case, the block captures self , ie it saves a copy of self as another strong pointer. That increases the retain count of the pointed-to object, and causes the retain cycle.

In the second case, the block captures me , ie it saves a copy of me as another weak pointer. That does not increase the retain count and therefore causes no retain cycles.

(If you print the address of me outside and inside the block, you will see that the addresses are different. The block has its own weak pointer to the object.)

If the pointed-to object is deallocated, all weak references (including the one saved by the block) are set to nil by the Objective-C runtime.

(I just hope that I got this right.)

A retain cycle happens when two objects store a strong reference to each other. The simplest case is object a storing a strong reference to object b and b doing the opposite [1]. Retain cycles are a problem in Objective-C because they make ARC believe that these objects are always in use even when these objects are not referenced from anywhere else.

Let's review some examples. You have object z which allocates a and b , makes use of them, and then disposes them. If a and b created a retain cycle between themselves in the first place, a and b won't be deallocated. If you do that several times you would be seriously leaking memory.

Another real world example of a retain cycle is if a allocates and strongly references a b object, but you also store a strong reference from b to a (many smaller objects in the object graph may need to access their parents ).

The most usual solutions in these cases would be to make sure that contained objects only have weak references to its containing objects, and also make sure that sibling objects don't contain strong references to each other.

Another solution (generally less elegant, but possibly appropriate in some situations) could be having some kind of custom cleanup method in a that nils its reference to b . Thus b would get deallocated when cleanup is called (if b is not strongly referenced elsewhere). This is cumbersome because you cannot do this from a 's dealloc (it never gets called if there is a retain cycle) and because you have to remember to call cleanup at appropriate times.

  1. Note that retain cycles are also transitive (eg, object a strongly references b which strongly references c which strongly references a ).

With all this said: memory management of blocks is quite tricky to understand.

Your first example could create a temporary retain cycle (and only if your self object stores a strong reference to someObject ). This temporary retain cycle goes away when the block finishes execution and is deallocated.

During execution, self would store a reference to someObject , someObject to the block , and the block to self again. But again, it is only temporary because the block is not permanently stored anywhere (unless [someObject successBlock:failure:] implementation does that, but that is not frequent for completion blocks).

So, the retain cycle is not an issue in your first example.

Generally, retain cycles within blocks are only an issue if some object is storing the block rather than executing it directly. Then it's easy to see that self strongly references the block and the block has a strong reference to self . Note that accessing any ivar from inside a block automatically generates a strong reference to self in that block.

The equivalent to making sure that the contained object does not strongly reference its container is using __weak SelfClass *weakSelf = self for accessing both methods and ivars (the better if you access ivars through accessors, as when using properties). Your block's reference to self will be weak (it's not a copy , it's a weak reference ) and that will allow self to de deallocated when it's no longer strongly referenced.

It can be argued that it's good practice to always use weakSelf inside of all blocks, stored or not, just in case. I wonder why Apple didn't make this the default behavior. Doing this generally doesn't do anything harmful to the block code, even if actually unneeded.


__block is rarely used on variables that point to objects, because Objective-C doesn't enforce immutability of objects like that.

If you have a pointer to the object, you can call its methods, and these methods can modify it, with or without __block . __block is more (only?) useful on variables of basic types (int, float, etc.). See here for what happens when you use __block with an object pointer variable. You can also read more about __block in Blocks Programming Topics by Apple.

Edit: Fixed mistake regarding __block usage on object pointers. Thanks to @KevinDiTraglia for pointing it.

Your first example will not create a never ending retain cycle. There will be retain cycle, all right, but once the blocks are done, the reference form the blocks to the someObject will be removed. So the someObject will live at least until the blocks are done. Such temporary retain cycle could be a good or bad thing, depending on what you want:

If you need your someObject alive at least until its blocks completions, it's okay. However, if there's no reason to keep that object, you should implement it using 'weak' referencing.

Eg. myObject is a view controller which in those blocks fetches a picture from the net. If you pop that someObject form the navigation controller, the controller won't be able to display the picture after fetching it, so there's no need to keep it. The success or error are irrelevant, user is not anymore interested in the picture someObject was supposed to fetch. In such case, the usage of weak is better option, however the code in blocks should expect than self could be nil.

You can path self as block's argument, exactly giving variable name 'self', this will protect from selfretaining in block.

And you are whrong with 'someObject and self never get de-alloced': self will be released when blocks deallocated. Blocks will be deallocated with someObject. SomeObject will be deallocated when it have no more references. So if your self-object owns someObject, just release someObject when you don't need it any more.

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