iOS Diary: 5 Tips for Leak-free Apps

So you want to do some iOS development, but you’ve heard that memory management is a chore? Have no fear. All you have to do is keep track of a reference count on every object in your application, making sure that when the last live pointer to each dies, the object has a ref count of 0 so that it can be deallocated. Ok, so maybe it does suck. If you’re used to modern tools like C# or Java then this world is a real throwback. Fortunately Apple provides some awesome tools for tracking down problems with leaks, and if you follow the right conventions you too can keep your objects from lying around and growing moldy. Since I just went through my first iOS project, and am now an expert, here are five things I’ve learned about iOS memory management:

1. Know the difference between alloc/init and with/by/from/of methods.

The alloc and init methods are the standard means for creating an object on the heap. Some common forms are:

MyObject *anObject = [[MyObject alloc] init];
MyObject *anObject = [[MyObject alloc] initWithSomeText:text];
MyObject *anObject = [[MyObject alloc] initWithColor:[UIColor blueColor]];

One thing that is true about all forms of alloc/init is that they return an object with a reference count of 1. You own that object, and are responsible for releasing it. By contrast what I called the with/by/from/of methods are factory methods that you call to get an object. A few examples you might have seen:

NSString *myString = [NSString stringWithFormat:@"%@", someObject];
NSString *myString = [anotherString stringByAppendingString:aThirdString];
UIFont* aFont = [UIFont boldSystemFontOfSize:17];

Most methods of this form return an autoreleased object. Unless you retain the result then it’s going to be released and deallocated when the next event loop runs. So when do you want to own an object, and when do you want one that is autoreleased? Consider a scenario where you’re passed a string in a callback, and are going to assign it to a property and keep it around. By convention, when you are passed an object in an argument to a method call you don’t own it. You don’t know what the caller is going to do to that object after the method returns. So you want to copy it to your property. Here is one way I’ve seen that done:

@interface MyClass {
}
@property (nonatomic, retain) NSString *myProperty;
@end

@implementation MyClass
@synthesize myProperty;

-(void)somethingSureMessedUp:(NSString *)reason {
    self.myProperty = [[NSString alloc] initWithString:reason];
}
@end

The question is: will that code leak? The answer is yes, it will. The initWithString method returns a new object with a reference count of 1. It is then assigned to a retained property. When an object is assigned to a retained property two things happen: the existing value is released, and the new value is retained. So the string now has a reference count of 2, and even if you follow my tip on releasing retained properties it will still leak. The alternative is the factory method:

-(void)somethingSureMessedUp:(NSString *)reason {
    self.myProperty = [NSString stringWithString:reason];
}

The stringWithString method also returns an object with a reference count of 1, so when we assign it to the retained property it still has a reference count of 2. But before the method returned our shiny new string it called autorelease on it. So when we return from this method and the autorelease pool gets processed this sting is getting a release call, and the reference count will be back to 1. Result: no leak.

So, when do you use init/alloc? Sometimes there isn’t a convenient factory method and you don’t have a choice. In those cases you may want to follow the pattern in Tip 2. In other scenarios you may have an interface component, or a collection, whose lifetime is the same as the containing class. In those cases feel free to call init/alloc and then release the reference in your container’s dealloc.

2. Autorelease what you don’t retain

This is the corollary to Tip 1: if you write a method that produces an object, and you don’t specifically keep a reference to that object and ensure that it is released on dealloc, then autorelease it before letting the reference go. In the context of the example above, if there was no convenient factory method that returned an autoreleased object you could follow another common pattern:

-(void)yourDaughterTookTheCar:(NSString *)reason {
    self.myProperty = [[[NSString alloc] initWithString:reason] autorelease];
}

In this case you’re doing the same thing that the factory method does internally. Your autorelease will balance out the retain that the property setter will do, and nothing will leak, assuming you follow Tip 5.

3. Use properties

Sure, everyone uses properties when they want to make something available to another class, but what about when you just have some internal objects to keep track of? In general, unless a reference has exactly the same lifetime as its container class, i.e. the object it points to is created when the container is created, and deallocated when the container is deallocated, then it makes more sense to keep it in a property than to use an ivar. This is especially true if the reference is going to point to different objects over the lifecycle of the program.

Retained properties retain and release their objects, so assigning a new value to one automatically releases the previous value. This behavior makes it much easier to keep the reference counts of objects with transient lifetimes balanced. The follow-up rule to this one is Tip 4.

4. Always use property access syntax to access properties

I wrote a short post on this one a few weeks ago. Properties are backed up by ivars, and ivars are accessible within the implementation of the class that declares them. It’s all too easy to do this:

-(void)sheBetterBringItBack:(NSString *)reason {
    myProperty = [NSString stringWithString:reason];
}

Outside callers can’t do this, because they can’t see the ivar (unless its public, and it shouldn’t be) and have to use property access syntax. Although you can get away with this inside the declaring class, its either a leak or a crash waiting to happen. The problem is that this notation (leaving out “self.”) bypasses the property setter completely. There is no operator overloading in C, and C is all Objective-C is in the end. The only way to make sure that you call the setter and properly retain the object is to use . syntax to reference the name. Of course you could do this:

-(void)somethingSureMessedUp:(NSString *)reason {
    myProperty = [[NSString stringWithString:reason] retain];
}

But why would you? You’re just doing an end-run around the property mechanism, and you might as well work with it rather than against it. Declaring and using properties correctly can help a lot with the management of object reference counts. My last tip involves what to do with properties at the end of the world.

5. Release retained properties in dealloc

This one might seem like a no-brainer, but it has caught me a couple of times (which doesn’t prove that it isn’t a no-brainer), especially when adding new retained properties to an existing class. The property mechanism only comes into play when a property is set. At the end of the world, when your container object’s dealloc method is called, remember to go through your retained properties and release any that aren’t nil. You can either release them explicitly, or by setting them to nil, but either way they remain your responsibility.

And that’s it, except for one last bonus tip: learn to use the Instruments framework in XCode to profile your memory usage. Apple is pretty strict about app behavior, and leaks can get your work rejected. The leak and allocations analysis tools are really very impressive, and remind me of using Bounds Checker back in the day. They are too big a topic to tack onto the end of this post, but Apple’s developer site has some great overviews. Check them out.

2 thoughts on “iOS Diary: 5 Tips for Leak-free Apps

  1. I think your claim that this will leak:

    -(void)somethingSureMessedUp:(NSString *)reason {
    self.myProperty = [[NSString alloc] initWithString:reason];
    }

    …is not correct. Doesn’t the synthesized setter method of myProperty handle this? I note, for example, that XCode’s own generated code uses this approach for its synthesized window property:

    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    Please note, I’m also relatively new to IOS and looking for clarification on practically everything.

  2. Actually, Mark, forget I mentioned this. I see the distinction now. Almost all the Apple sample code I’ve been looking at where they’re alloc to an instance variable is in init, where they’re explicitly avoiding the setter method (for other reasons), thereby avoiding that method’s retain mechanism.

    Thanks a lot for pointing this out. It’s one of those subtleties a newbie like me could have easily missed without reading your post.

    Wayne

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>