8

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?

hodgesmr
  • 2,765
  • 7
  • 30
  • 41

4 Answers4

7

In the first case, the block captures self, i.e. 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, i.e. 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.)

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • assuming that the MyCLass implements a copy that is a real copy... because `-copyWithZone:` can just retain... which is perfectly legit and done in just about any immutable object. – Grady Player Feb 16 '14 at 15:57
  • @GradyPlayer: Perhaps I expressed myself badly, but what I meant is that the block saves a strong (or weak) pointer in its block context with the *current contents* of `self` (or `me`). The `copy` *method* is not involved. – Martin R Feb 16 '14 at 16:10
  • Yeah sometimes SO cycles stuff back to the top when someone does something to them... and sometimes I have to have a nerd nit-pick months or years later... but objects can be copied on block capture so I don't think that is incorrect... – Grady Player Feb 16 '14 at 16:34
  • @GradyPlayer: You don't think that is incorrect? Or you don't think that is correct? – Martin R Feb 16 '14 at 16:39
  • I think capturing an object can but doesn't necessarily cause the object in the block to have a new address. – Grady Player Feb 16 '14 at 17:06
  • @GradyPlayer: Are you thinking of the address of the pointer itself, or the address of the pointed-to object? I don't think that capturing (the pointer to) an object can cause the object to be copied (in the sense of the `copy` method). - But of course I may be wrong ... – Martin R Feb 16 '14 at 17:13
4

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 (e.g., 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.

Community
  • 1
  • 1
Ricardo Sanchez-Saez
  • 9,466
  • 8
  • 53
  • 92
  • 1
    Nice answer, but are you sure about that last statement? I am looking into an issue of using __block instead of __weak for a reference type and they have different behaviors, the __weak reference becomes nil while the __block reference does not. I'm thinking it's closer to a strong pointer for object references. – Kevin DiTraglia Jan 22 '14 at 18:32
  • Thanks for your comment, you are right. I fixed that bit of the answer. – Ricardo Sanchez-Saez Sep 19 '14 at 12:47
  • Not sure if making always weak reference to self is correct. Sometimes I think you may want that block retains the reference, so it will not let it to be deallocated. As far as I understand it should be used only when using a strong reference will cause a retain cycle. – User Mar 08 '15 at 10:57
3

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.

Maksymilian Wojakowski
  • 5,011
  • 3
  • 19
  • 14
  • 1
    Isn't it more accurate to say that once the blocks are done, the reference to *them* is removed? – User Mar 08 '15 at 11:03
  • This is indeed correct. +1 because it explains why it doesn't create a perm retain cycle. Many new programers are always using weakSelf because they are misinformed on the retain cycles like listed in the accepted answer. While this is alright for most applications, more complex applications will see issues with references being deallocated before the block is done being executed causing crashes if you try to reference those objects later. I think you meant to say in the last sentence that `weakSelf` could be nil. – Bot May 03 '16 at 23:24
0

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.

Cy-4AH
  • 4,370
  • 2
  • 15
  • 22