I'm just getting started with blocks and Grand Central Dispatch. I've been told (and read in the Apple Documentation) that any object referenced from within a block gets retained.
For instance:
^{
self.layer.transform = CATransform3DScale(CATransform3DMakeTranslation(0, 0, 0), 1, 1, 1);
self.layer.opacity = 1;
}
"self" gets retained so it leaks. To avoid that, I need to assign self to:
__block Object *blockSelf = self;
and then use blockSelf
instead of self
inside my block.
My question is: what happens when your block has a lot more code and references several objects? Do I need to assign them all to __block
objects? For instance:
^{
[self doSomething];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"prevName == %@", artistName];
[request setEntity:entity];
[request setPredicate:predicate];
Object *newObject = [[Object alloc] init];
[someArray addObject];
[newObject release];
}
The retainCount is the number of ownership claims there are outstanding on the object. You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message.
Blocks are a language-level feature added to C, Objective-C and C++, which allow you to create distinct segments of code that can be passed around to methods or functions as if they were values. Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary .
No, a copied object will have a retain count of 1, just like a newly initialized object.
You'd use alloc, retain and release to indicate which objects needed to be retained or released. The retain method would increase an object's retain count, and release would decrease it. Just as with ARC, a retain count of zero meant the object was removed from memory. These days, ARC automates this process.
No. The problem occurs when your block retains an object which retains it. Your block will retain any object that it references, except for those annotated with __block
. Hence:
// The following creates a retain cycle which will leak `self`:
self.block = ^{
[self something];
};
self
retains block
, and block
implicitly retains self
. This will also
happen if you reference instance variables of self
.
// The following avoids this retain cycle:
__block typeof(self) bself = self;
self.block = ^{
[bself something];
};
Variables annotated with __block
are mutable (for pointers, that is, the
address they point to can be changed); as a result, it makes no sense to
retain that object, since you need to treat that object as a local variable
(as in, it can be reassigned, affecting an object outside the scope of the block). Thus, __block
don't get retained by blocks.
But, now you can run into unforeseen problems if you try to use this block in certain ways. For instance, if you decide to delay the invocation of this block somehow, and self
has been deallocated by the time you execute that block, your program will crash, since you're sending a message to a deallocated object. What you need then is a weak reference, which is not provided out-of-the-box in the non-garbage-collected environment!
One solution is to use MAZeroingWeakRef to wrap your block; this will zero out the pointer so that you'll just end up sending messages to nil
should you attempt to message self
after self
has been deallocated:
MAZeroingWeakRef *ref = [MAZeroingWeakRef refWithTarget:self];
self.block = ^{
[ref.target something];
};
I've also implemented a weak reference wrapper in Objective-C++, which provides the benefit of a more lightweight syntax:
js::weak_ref<SomeClass> ref = self;
self.block = ^{
[ref something];
};
Because js::weak_ref
is a class template, you'll get handy strong-typing (that is, you'll get warnings at compile-time if you try to send the reference a message which it doesn't appear to respond to). But Mike's MAZeroingWeakReference
is a lot more mature than mine, so I'd suggest using his unless you want to get your hands dirty.
To read more about issues with __block
and the use-case for weak references, read Avoiding retain cycles with blocks, a right way and Jonathan Rentzsch's response.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With