iOS Memory Management Tipson 6 March 2011

I have been working in iOS extensively for two months straight. These short two months I have worked on a complex interactive view controller-based class. This class has multiple subviews and therefore has complex memory management. The following is hopefully more of a real guide to memory management and the small “gotchas” that are not clearly outlined in the documentation.

Memory Management in iOS as represented by elephants in as an iPhone background

Memory Management

Tip #1

If your class calls alloc on any object contained within it, release must be called. This could be in the form of release or autorelease.

UIView aView = [[UIView alloc] initWithFrame:frame]; // retain count 1
[self.view addSubview:aView]; // retain count 2
[aView release]; // retain count 1
 

I think this example is pretty straight forward, as far as memory management is concerned. aView is created and assigned. Then it is released on the third line.

Tip #2

When assigning newly allocated objects to class variables, make sure they follow Tip #1 or are autoreleased.

//header file
@interface CustomViewController : UIViewController {
  UIButton *saveButton;
}

@property (nonatomic, retain) UIButton saveButton;

@end

//implementation file
@implementation CustomViewController

@synthesize saveButton;

...
//inside a method
[self setSaveButton:[[[UIButton alloc] init] autorelease]]; // retain count 1
...

- (void)dealloc {
  [saveButton release]; // retain count 0
  [super dealloc];
}

@end
 

One of the bigger “gotchas” that added an extra retain count to objects in my project was the missing autorelease when the button was assigned to the class variable. This follows Tip #1 because alloc was called, meaning the class is responsible for the button. If the autorelease was missing when the setSaveButton: method is called the retain count would increased by 1 for a total of 2. One from the alloc and one from the assignment to saveButton.

If the button was created with the autorelease and not assigned to saveButton by the end of the method where it was allocd, the button would be released from memory. The autorelease would execute because there is nothing retaining a count to the button.

Tip #3

There are many cases for this next tip, one that springs to mind is when creating custom headers for the UITableView. It requires a UIView to be passed back for display as a header in the table. I do not want my table view delegate holding a retain count for the header.

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
  CustomUIView *v = [[CustomUIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 120.0, 120.0)]; // retain count 1
  [v setCustomSetting:100.0];
  return v; // incorrect implementation
}
 

The code above is incorrect. The method created the CustomUIView and never released it. So when the method calling this gets the view, it has an extra retain count and will lead to a memory leak. Worse yet, the development tool Instruments.app might not catch this. The correct implementation is below.

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
  CustomUIView *v = [[[CustomUIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 120.0, 120.0)] autorelease]; // retain count 1
  [v setCustomSetting:100.0];
  return v; // variable `v` will be autoreleased, unless the caller of this method retains the returned view (which the `UITableView` handles)
}
 

Tip #4

Similar to Tip 2, this tip is about class variables. Working with Flex has taught me a lot about getter and setter methods. iOS has @synthesize to create the getter and setter methods automatically. It is a great feature, though if not expecting it, problems can occur.

Using the example from Tip 2:

//header file
@interface CustomViewController : UIViewController {
  UIButton *saveButton;
}

@property (nonatomic, retain) UIButton saveButton;

@end

//implementation file
@implementation CustomViewController

@synthesize saveButton;

...
//inside a method
self.saveButton = [[[UIButton alloc] init] autorelease]; // using Objective-C 2.0 syntax

// then later, setting a new button

saveButton = [[[UIButton alloc] init] autorelease]; // setting instance, not class variable
...

- (void)dealloc {
  [saveButton release];
  [super dealloc];
}

@end
 

When we set directly to saveButton (instead of self.saveButton) we bypass the setter method that was defined with @synthesize. So the previously allocd UIButton would not be explicitly released because of the bypass, therefore creating an extra instance of a UIButton that is no longer able to be deallocated.

  • saveButton is the variable set in the @synthesize setter.
  • self.saveButton is the same as [self setSaveButton:], both use the @synthesize setter.

This can definitely lead to sticky situations when the setter is not called.

Tip #5

Certain C types create memory allocations and are required to be removed from memory by a specific method call. I do not pretend to know all of them, though one of them I do know of is a path in a drawing context. The following code creates a mutable path, adds points to the path and then closes the path. The value of this is being able to reuse the custom path as many times as needed until it is released.

CGMutablePathRef path = CGPathCreateMutable(); // created memory allocation
// many `CGPathAdd...` method calls to create the path
CGPathCloseSubpath(path);

CGPathRelease(path); // released path allocation
 

Create + Release

The main rule to follow is if a class calls alloc on an object it will be in charge of releasing that object or making sure it will be autoreleased. This is of course an exception with C types, because they have no autorelease.

I am still learning as time goes on. This list is things I have learned that tripped me up and will work for iPhone and iPad. Until this point, I was relying heavily on the Leaks module inside of Instruments. It failed me; Allocations actually helped a lot more. That was how I learned about what objects were being released and which ones were not. I could see items be created and never removed. Each time an action was performed, more allocations were created of objects that should have been only one in number. That is definitely a post for another time.

Updated March 6, 2011 9:20pm: Added Tip 4 and moved the old Tip to number 5.

If you enjoyed this, use this shorter link to share: http://the.ichibod.com/s/memios