Best memory-management questions in April 2011

iOS 4 blocks and retain counts

8 votes

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];
}

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 have an IBOutlet, but not a property, is it retained or not?

7 votes

I find the documentation on this issue to be unclear:

Say you are working with iOS (NOT the Mac case, no need to mention the differences). Say it is strictly 4.0+ (no need to mention differences in old OS). Say we are loading the NIB strictly automatically.

Say you have a UIViewController, BigView. Say there are a dozen so-called "top-level" items in the NIB file .. could be custom controls, images, or anything else.

Say you are definitely going to explicitly create and then get rid of BigView a number of times during the app's run. So:

For one of these top-level items in the NIB, there are three possibilities:

(1) You do not have any sort of IBOutlet for it, at all.

(2) You do have a connected IBOutlet - but not a property.

(3) You do have a connected IBOutlet property (to avoid confusion, we'll say a retain property).

So what happens to the item when BigView is released?

In the case of (3) it seems clear that you must release explicitly. If you do not, it will hang around after the view is gone. No problem.

In the case of (1) I assume (but can anyone actually confirm?) that the item will be released when BigView is gone.

In the case of (2) it's not clear what happens.......

Looking at the well-known reference link: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html it is very dubious:

"In iOS, the nib-loading code uses the setValue:forKey: method to reconnect each outlet. That method similarly looks for an appropriate accessor method and [SO WHAT HAPPENS IF THERE ISN'T ONE?? TELL US APPLE...] falls back on other means when that fails...[GOOD GRIEF!]"

And check out this documentation: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html and scroll down to "Nib Object Retention"

So ...

"Objects in the nib file are created with a retain count of 1 and then autoreleased" Fantastic..

But wait! Read on a few words...

however, ... which uses the available setter method or retains the object by default if no setter method is available

What are they talking about?

Do they mean that if no setter is available (ivar, but no property), that it is AGAIN RETAINED (other than the "retain" they just mention in the previous clause) --- or, are they just repeating themselves, i.e. the "retains the object by default" is the same "retain" they were talking about immediately previously ("created with a retain count of 1 and then autoreleased").

And why would they even mention the autorelease if that's not what happens?

Indeed -- if anyone actually specifically knows the answer to this question ...... how do you know?!? Did you ask DTS, or through testing, or? I suggest, the key documentation (just pasted in) is aggressively unclear.

Again - if you have an IBOutlet, but not a property, connected to a "top-level" object .. are you responsible for releasing it? Is it retained? in that situation?

For that matter .... merely in situation (1) is it absolutely the case that the thingy will be released when BigView goes away? I would certainly assume this is the case, but who knows?

The question is what happens if you DO use an IBOutlet iVar, but NOT a property...

I've foolishly never thought about this before / assumed too much, does anyone have the decisive answer? Cheers!!


For the record I have made a test project.

In fact (surprisingly to me) the mere act of connecting an IB element to an IBOutlet in fact apparently adds one retain.

(I can only assume from the shoddy docu, in that situation you get specifically: Retain, Autorelease, Retain - leading to one retain on balance.)

So, that's the answer.

I will post the demo project. I also direct any readers to Jonah's answer below which flawlessly explains the behavior of setValue:forKey: Cheers

I don't see what causes so much confusion, I think the "Nib Object Retention" documentation explains exactly what happens. Let's break it down and walk through what happens:

Objects in the nib file are created with a retain count of 1 and then autoreleased.

ClassLoadedFromNib *loadedObject = [[[ClassLoadedFromNib alloc] initWithCoder:coder] autorelease];

As it rebuilds the object hierarchy, however, UIKit reestablishes connections between the objects using the setValue:forKey: method,

[filesOwner setValue:loadedObject forKey:nameOfIBOutlet];

which uses the available setter method or retains the object by default if no setter method is available.

The default behavior of -setValue:forKey: in iOS is roughly

//lazy pseudocode
if ([self respondsToSelector:@selector(@"setKeyName:")]) {
  [self setKeyName:value];
}
else {
  object_setIvar(self, _keyName, [value retain]);
}

See the key-value programming guide for even more detail. Unless your file's owner object overrides -setValue:forKey: (or +accessInstanceVariablesDirectly and -setValue:forUndefinedKey: ) expect object ownership to be managed as above.


If you define outlets for nib-file objects, you should always define a setter method (or declared property) for accessing that outlet. Setter methods for outlets should retain their values, and setter methods for outlets containing top-level objects must retain their values to prevent them from being deallocated.

Allowing nib loading to set ivar directly to externally retained objects is confusing. Don't do that. Provide setter methods for your outlets so the ownership of the loaded object is clear.


If you do not store the top-level objects in outlets, you must retain either the array returned by the loadNibNamed:owner:options: method or the objects inside the array to prevent those objects from being released prematurely.

Objects not connected to outlets have been autoreleased. Retain them or the array returned from -loadNibNamed:owner:options: if you are going to try to access them later.