I am working through some retain-cycle issues with blocks/ARC, and I am trying to get my head around the nuances. Any guidance is appreciated.
Apple's documentation on "Blocks and Variables" (http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html) says the following:
If you use a block within the implementation of a method, the rules
for memory management of object instance variables are more subtle:
If you access an instance variable by reference, self is retained; If
you access an instance variable by value, the variable is retained.
The following examples illustrate the two different situations:
dispatch_async(queue, ^{
// instanceVariable is used by reference, self is retained
doSomethingWithObject(instanceVariable);
});
id localVariable = instanceVariable;
dispatch_async(queue, ^{
// localVariable is used by value, localVariable is retained (not self)
doSomethingWithObject(localVariable);
});
I find this explanation confusing.
- Is this appropriate use of the "by value" / "by reference" terminology? Assuming that these variables are of the same type (id), it seems like the distinguishing characteristic between them is their scope.
I do not see how self is being referenced in the "by reference" example? If an accessor method were being used, (e.g. - below), I could see self being retained.
doSomethingWithObject(self.instanceVariable);
Do you have any guidance on when one might want to do things one way or the other?
- If conventional wisdom is to utilize the "by value" variables, it seems like this is going to result in a lot of additional code for additional variable declarations?
- In a circumstance where nested blocks are coming into play, it seems like it may be more maintainable to avoid declaring the blocks inside of each other as one could ultimately end up with a potpourri of unintentionally retained objects?
Think of the case of using instanceVariable
as an equivalent of writing self->instanceVariable
. An instance variable is by definition "attached" to the self
object and exists while the self object exists.
Using instanceVariable
(or self->instanceVariable
) means that you start from the address of self, and ask for an instance variable (that is offset of some bytes from the self
object original address).
Using localVariable
is a variable on its own, that does not rely on self and is not an address that is relative to another object.
As the blocks capture the variables when they are created, you typically prefer using instance variables when you mean "when the block is executed, I want to get the value of the instance variable at the time of the execution" as you will ask the self
object for the value of the instance variable at that time (exactly the same way as you would call [self someIVarAccessorMethod]
). But then be careful not to create some retain cycles.
On the other hand, if you use localVariable
, the local variable (and not self
) will be captured when the block is created, so even if the local variable changes after the block creation, the old value will be used inside the block.
// Imagine instanceVariable being an ivar of type NSString
// And property being a @property of type NSString too
instanceVariable = @"ivar-before";
self.property = @"prop-before";
NSString* localVariable = @"locvar-before";
// When creating the block, self will be retained both because the block uses instanceVariable and self.property
// And localVariable will be retained too as it is used directly
dispatch_block_t block = ^{
NSLog(@"instance variable = %@", instanceVariable);
NSLog(@"property = %@", self.property);
NSLog(@"local variable = %@", localVariable);
};
// Modify some values after the block creation but before execution
instanceVariable = @"ivar-after";
self.property = @"prop-after";
localVariable = @"locvar-after";
// Execute the block
block();
In that example the output will show that instanceVariable
and self.property
are accessed thru the self
object, so self was retained but the value of instanceVariable
and self.property
are queried in the code of the block and they will return their value at the time of execution, respectively "ivar-after"
and "prop-after"
. On the other hand, localVariable
was retained at the time the block was created and its value was const-copied at that time, so the last NSLog
will show "locvar-before"
.
self
is retained when you use instance variables or properties or call methods on self itself in the code of the block. Local variables are retained when you use them directly in the code of the block.
Note: I suggest you watch the WWDC'11 and WWDC'12 videos that talks about the subject, they are really instructive.