If a block refers to a member of an object then the blocks takes ownership of the object. If the object has also taken ownership of the block to keep it alive for at least the lifetime of the object then this creates a strong reference cycle. Possible solutions are:

  • Create a local variable within the scope where the blocks is implemented or have something like a cancel method on the object that nils the member that is the one that owns the block.
  • Or you can use weak references in the scope where the block is implemented and then assign a strong reference within the block scope and check to see if the reference is nil or not before deciding on what work to do within the block.

Using the following simple block definition we can explore:

typedef void (^mySimpleBlockType)(void);

@interface BlockOwner : NSObject
@property (copy) mySimpleBlockType blockProperty;
@property (strong) NSString ownerName;
@end

Specifying (copy) as in above is not required under ARC but is required if not using ARC. It is still good practice with ARC because that is the behaviour that will happen. The block will need to be copied from the stack to the heap.

When capturing self in a callback block you must be careful not to create a strong reference cycle. The following examples both create strong reference cycles to self.

Ex1:

@implementation BlockOwner
-(void) configureBlock
{
  self.blockProperty = ^{ [self doSomething]; };
}
@end

Ex2:

@implementation BlockOwner
-(void)configureBlock
{
  self.blockProperty = ^{ self.ownerName = @"Mr Big"; };
}
@end

The first method will fix both Ex1 and Ex2.

Ex1:

@implementation BlockOwner
-(void)configureBlock
{
  BlockOwner * __weak weakSelf = self;
  self.blockProperty = ^{
  BlockOwner *strongSelf = weakSelf;
  if (strongSelf)
    [strongSelf doSomething];
  };
} 
@end

Ex2:

@implementation BlockOwner
-(void)configureBlock
{
  BlockOwner * __weak weakSelf = self;
  self.blockProperty = ^{
    BlockOwner *strongSelf = weakSelf;
    if (strongSelf)
      NSLog(@"BlockOwner name: %@", strongSelf.ownerName);
  };
} 
@end

This second method will fix Ex2 but not Ex1:

@implementation BlockOwner
-(void)configureBlock
{
  NSString *capturedOwnerName = self.ownerName;
  self.blockProperty = ^{
    NSLog(@"BlockOwner name: %@", capturedOwnerName);
  };
}
@end

A third method requires keeping track of when the block is no longer needed and clearing the block property. This will be when the operation that calls back to the block is completed, perhaps the last action of the block is to clear block property of the captured block or when the operation that requires the block is cancelled or if the block is called repeatedly then when it is known the block is no longer required the block property should be cleared.

In the cancel method, and/or in the block or cleanup after repeated calls of the block we should do:

self.blockProperty = nil;

The __block attribute under ARC in terms of keeping an object alive behaves like __strong.

So if we have a one shot block we can do the following to break the strong reference cycle:

@implementation BlockOwner
-(void)configureBlock
{
  __block BlockOwner *modifiableiVar = self;
  self.blockProperty = ^{
    NSLog(@"BlockOwner name: %@", modifiableiVar.ownerName);
    modifiableiVar = nil; // This breaks the strong reference cycle.
  };
}
@end