Home > Articles > Home & Office Computing > Mac OS X

  • Print
  • + Share This
This chapter is from the book

Block Retain Cycles

Blocks have retain counts. If you have retain counts, you can get retain cycles. Here is a typedef for a simple block, and a class that references the block, along with a string instance variable.

typedef void (^BoringBlock)(void);

// The leaky object.
@interface Leakzor : NSObject {
    NSString *_string;
    BoringBlock _blockhead;
}

// Print |string|.
- (void) funk;

@end // Leakzor

And here are -init and -dealloc.

- (id) init {
    if ((self = [super init])) {
        _string = @"snork";

        // |string| is same as self->string, so |self| is retained.
        _blockhead = Block_copy(^{
                NSLog (@"string is %@", _string);
            });
    }
    return self;
} // init

- (void) dealloc {
    [_string release];
    [_blockhead release];
    [super dealloc];
} // dealloc

The _string instance variable is used in the block. Instance variable references like this are actually a pointer dereference off of self, so

NSLog (@"string is %@", _string);

is the same as

NSLog (@"string is %@", self->_string);

Because self is a variable in the outside scope that is being captured, it is retained. self will not be released until the block is released in -dealloc, but -dealloc will not be called until self has been released. This is the cause of the retain cycle.

You can break the retain cycle by using a __block-scoped local variable that points to self. __block-scoped objects are not retained automatically, so the retain cycle is not created:

- (id) init {
    if ((self = [super init])) {
        _string = @"snork";

        // blockSelf is __block scope, so won't be auto-retained
        __block Leakzor *blockSelf = self;
        _blockhead = Block_copy(^{
                NSLog (@"string is %@", blockSelf->_string);
            });
    }
    return self;
} // init

You do not have to jump through this hoop for every block you make because most blocks have a very short lifespan. Just be aware that if you have a block that has the same lifetime as its owning object and you are freeing the block at -dealloc time, you can get a retain cycle.

  • + Share This
  • 🔖 Save To Your Account