James's Twisted Ape

Thoughts, code and anything else.

Blocks and Memory Management

What happens when you create a block?

Normally when you write a block in Objective C, the compiler actually creates an Objective C object, however the object is likely different to any other Objective C object that you will have seen. One of two types of block will be created:

  1. If the block doesn’t use any variables from outside its scope a global block will be created. In concept this works exactly the same as a nameless function; it’s an area of code that you can call into and is always there, it never gets destroyed. If you were to save the address of this block when creating it you could call into this code at any time during the lifetime of your program. As you don’t reference any variables from outside the block, there are no issues with memory management. These blocks are not interesting from a memory management point of view, so will be mostly ignored in the rest of the blog post.

  2. If the block references any variables that are declared outside the block then the block will be created as a stack object. This isn’t something that exists in the rest of the Objective C world. The rest of the time when you create an object, memory is put on the heap, not the stack (other languages do support stack objects as a normal concept).

The block being a stack object has some nice advantages. It’s always really fast to create the object; the program’s stack is calculated in advance so there is no need to do any work to find contiguous memory to allocate for the object, it’s already there. The second (and more important) is that all of the object’s memory drops off the stack once you leave the current stack frame, i.e. when you return from your function or method. This means that in a manual reference counted (MRC) environment you can create the block object without having to keep a reference to it to manually free its memory, i.e. you can do this:

1
2
3
4
5
NSArray *array = [NSArray arrayWithObject:@"testing"];

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  NSLog (@"%@", array);
}];

We don’t need to worry about the object that this has secretly created because all of that object’s memory will be cleared once we leave the function.

This is very different to other objects on the heap. In an MRC environment the following will cause you to leak memory:

1
NSArray *array = [NSArray arrayWithObject:[[NSView alloc] init]];

We have created an NSView but have no way to release it again. To solve this we would need to do the following:

1
2
3
NSView *view = [[NSView alloc] init];
NSArray *array = [NSArray arrayWithObject:view];
[view release];

So by making blocks secretly stack objects, they can work nicely with manual reference counting with a nice syntax where we appear to be just adding a block of code as an argument.

Another important thing to note is that because blocks are secretly stack objects, they can’t take ownership (i.e. increment the retain count) of any variables used inside the block that were declared outside the block (well at least not without the magic of ARC). The block objects aren’t deallocated; their memory is simply dropped when the method returns, so under MRC there is nowhere where the block could remove its ownership of the objects (which would normally be done in dealloc). This means that code like this:

Block Ownership
1
2
3
4
5
6
7
NSArray *array = [[NSArray alloc] initWithObjects:@"test", nil];
void (^block)(void) = ^{
  NSLog(@"%@", array);
};

[array release];
block();

may cause a crash as the array object has been deallocated. (If you’re very unlucky the array pointer will point at something that responds to the description method and you won’t get the crash.)

As the block is on the stack, keeping a reference to the block that outlasts the scope of the function/method won’t work:

Break Stack Block
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@implementation JTAObject
{
  void (^keepBlock)(void);
}

- (id)init
{
  if ((self = [super init])) {
    keepBlock = ^{
      // We need to reference `self` so we don't make a global block by mistake.
      NSLog(@"self = %@", self);
    };
  }

  [self performSelector:@selector(breakingBlockCode) withObject:nil afterDelay:3];

  return self;
}

- (void)breakingBlockCode
{
  keepBlock();
}

The above code causes a crash (without the reference to self within the block, a global block is created, so everything would work fine).

If you want to keep a block around outside of your method you need to call the copy method on the block. This method will copy the block from the stack to the heap. It will also retain all of the variables that the block references. This means that the block now owns the variables that are referenced inside the block. So if we change the block ownership code example a couple above then we won’t crash:

block ownership with copy
1
2
3
4
5
6
7
8
9
10
11
NSArray *array = [[NSArray alloc] initWithObjects:@"test", nil];
void (^block)(void) = ^{
  NSLog(@"%@", array);
};

void (^blockCpy)(void) = [block copy]

[array release];
blockCpy();

[blockCpy release];

There are a couple of things to note here. We need to keep a reference to our copied block; this block is now under the normal reference counting rules and we need to release it when appropriate. The variables declared outside the block that are used within the block have also had their retain counts incremented, so when we call blockCpy after releasing array we don’t crash.

What changes in ARC?

In ARC the memory management works differently. Suddenly with ARC there is the magical ability to deallocate the instance variables of the block when the block drops out of scope. This means that the block can have strong instance variables (i.e. it can increment the retainCount of its instance variables) without the risk of leaking memory.

When you create a block in ARC the block’s instance variables (the variables that were declared outside the block, and used inside the block) are declared with the same ARC Ownership Qualifiers as they were declared with outside of the block. This means that if you use a variable that is declared as __strong inside a block, the block will keep a __strong copy of the variable (i.e. the variable’s retainCount will be incremented). If however the variable’s ownership qualifier is __weak or __unsafe_unretained the variable’s retainCount won’t be incremented by the block (you can’t capture an __autoreleasing variable in a block). Here is code similar to the Block Ownership code from earlier, and it won’t crash:

Block Ownership ARC
1
2
3
4
5
6
7
NSArray *array = [[NSArray alloc] initWithObjects:@"test", nil];
void (^block)(void) = ^{
  NSLog(@"%@", array);
};

array = nil;
block();

The block has kept a reference to array because array is declared as __strong.

Let’s instead try passing in a __weak reference:

1
2
3
4
5
6
7
8
NSArray *array = [[NSArray alloc] initWithObjects:@"test", nil];
__weak NSArray *wkArray = array;
void (^block)(void) = ^{
  NSLog(@"%@", wkArray);
};

array = nil;
block();

This code will print (null) to the console. This is because when the object referenced by a __weak variable is deallocated the __weak variables associated with the object are set to nil. In this case when we do array = nil, wkArray is set to nil.

If instead we gave wkArray an ownership qualifier of __unsafe_unretained we’d likely get a crash (we’d definitely get a crash if Zombies are enabled). This is because wkArray now points to the location of an object that has been deallocated, and __unsafe_unretained doesn’t have the magic ability of __weak to make the variable nil when it is deallocated.

In some instances you may be thinking that your code should always have a reference to the variable that you have used in a block in a non-__strong way (i.e. you know that another __strong reference to the object exists when you call the block) and that you’d rather have a crash if there is no reference to the object. In this case I’d recommend using __weak and asserting that the variable exists within the block, as using __unsafe_unretained doesn’t guarantee you will have a crash when you try and reference the variable.

This makes the ARC situation with blocks very different to the MRC situation. When you use a __weak reference within a block it doesn’t matter if the block is on the stack or the heap, the block won’t take ownership of the object. Conversely if you use a __strong reference regardless of whether the block is on the heap or the stack the block will take ownership of the object.

The ability to use __weak references within a block allow you to avoid retain cycles. The following code will never cause a retain cycle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@implementation JTAObject
{
  void (^block)(void);
}

- (id)init
{
  if ((self = [super init])) {
    __weak JTAObject *weakSelf = self;
    block = ^{
      NSLog (@"self = %@", weakSelf);
    }
  }
}

If the block kept a __stong reference to self here, we would have a retain cycle.

However, because the block doesn’t keep a strong reference to our JTAObject, if something else has a reference to the block it’s possible for the block to be called after self has been deallocated. If this is a possibility in your code, due to the magic of weak references you can check whether weakSelf exists and exit the block early if it doesn’t.

The above applies regardless of whether the block is on the stack or the heap. The memory management is done entirely based on the ownership qualifiers of the objects referenced within the block.

It doesn’t really matter any longer whether a block is on the stack or the heap as ARC will clear things up anyway. It is however interesting to know what types of blocks we are creating:

  1. If you create a block and no variables are referenced within the block, then a global block is created and all reference counting (including ARC) is ignored. The following options assume that you reference a variable from outside the block.

  2. If you create a block and don’t keep a reference to the block (i.e. you pass it into a method immediately) then a stack block will be created.

  3. If you create a block and keep a __strong reference to the block (which is the default) the block will be immediately copied to the stack so that ARC can deallocate the block sensibly when it goes out of scope.

  4. If you create a block and keep an __unsafe_unretained reference then the block will be created on the stack like it is in MRC.

  5. If you try to create a block and only keep a __weak reference to it, the block will be moved to the heap and immediately deallocated, probably causing a crash. So this is a very bad idea.

If you’re interested can test the type of block you have by printing the class block in the debugger (i.e. po [myBlock class]). Global blocks have a class __NSGlobalBlock__, blocks on the heap have a class of __NSMallocBlock and blocks on the stack have a class of __NSStackBlock__.