August 3, 2009

Assigning Delegates

Kind reader Demitri Muna pointed out a flaw in my TableFormEditor package. I stumbled into a trap I fear many newbie Objective-C programmers also fall into: that of retaining delegates. It is a two-line change, but I feel this issue is important enough to write about it.

The basic problem is a retain cycle which results in a memory leak. This has been fairly well documented in this thread, but I will go into more detail here in the context of the TableFormEditor.

In this context, there are 2 actors involved:

  1. the client object
  2. the TableFormEditor object

When the client object creates a TableFormEditor instance, it needs to pass a delegate to handle the various callbacks. Typically, but not required, the delegate is the client object (i.e. self).

The TableFormEditor has this declaration in its .h file:

@property (nonatomic,retain) id <TableFormEditorDelegate> delegate; 

So what this means is that the client has a retain on the TableFormEditor (because it created it via [alloc]), and the TableFormEditor instance has a retain on the client (via the "retain" keyword in the @property declaration).

This is the dreaded A -> B and B -> A scenario described in the Stack Overflow thread mentioned above. When you are in this situation, A and B will never go away because B has a retain on A that will not release until B goes away. But B won't go away until A goes away. Result: memory leak!

However, all is not lost if you followed the pattern I have in the TableFormEditor example. In the example, I do not keep the TableFormEditor object around. As soon as I push it on the Navigation Controller's stack, I release it:

// stick the editor onto the navigator's stack 
[self.navigationController pushViewController:form animated:YES];      

// release the form since the navigator retained it 
[form release]; 

But, if you retain the pointer to the TableFormEditor, like keeping it in an instance variable, then you will likely end up with a memory leak.

Thus I have released version 1.6 of the TableFormEditor class to make this change:

@property (nonatomic,assign) id <TableFormEditorDelegate> delegate; 

This simply assigns the pointer without bumping up the retain count. This breaks the cycle and the potential for memory leaks.

For more information on retain cycles and how to prevent them, I urge you to check out this blog article at Cocoa With Love.