19

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];
}
samvermette
  • 40,269
  • 27
  • 112
  • 144

2 Answers2

48

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.

Jonathan Sterling
  • 18,320
  • 12
  • 67
  • 79
  • Thank you for the elaborate answer Jonathan. So if I'm understanding correctly, I don't even need to assign self to a __block variable as long as I'm not retaining the block for reuse? – samvermette Apr 02 '11 at 21:22
  • 1
    Yep (though I usually do just to be safe). So long as you use the block right then and there and don't copy it, you shouldn't have a problem. – Jonathan Sterling Apr 02 '11 at 21:39
  • Do you mind explaining this a bit more. If you look at this: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW1 in the Objective-C blocks section, it kinda points otherwise, unless I have read it wrong. I assume he is doing an animation which is used by a class method which, in results, do not retain the block by self hence making it safe. If he'd use any other implementation where he'd retain partially that block it would leak. – Pier-Olivier Thibault Apr 05 '11 at 01:51
  • @Pier-Olivier, I don't quite follow you. Not sure where animation comes into this. However, the rules for blocks are simple: 1. referenced variables not marked `__block` are `const`-copied (for objects, this means a copy of the pointer is made, not a copy of the object); 2. referenced objects not marked `__weak` or `__block` are retained. – Jonathan Sterling Apr 05 '11 at 04:11
  • 1
    Just remember: arc => `__weak` else => `__block`. – Nate Symer Jul 10 '13 at 01:00
2

I'd say it depends what you're doing with your block. If you're not storing it anywhere and use it in the definition place (like sorting an array using a block), then it gets released along with variables referenced inside it (since it's created in stack and is marked for autorelease). If you're storing it somewhere (an array, dictionary or probably pass the block to some other function) copy the block and balance it out with autorelease before passing it along.

Eimantas
  • 48,927
  • 17
  • 132
  • 168