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.

iOS Diary: Understanding the Application Lifecycle

Pretty much since the beginning of this project I’ve been aware of a bug in the iPhone app I am finishing. About a week in I figured out how to reproduce it. If you ran the app, then hit the home button to dismiss it, then tapped the program icon to bring it back and tried to switch tabs it would crash. If running in the debugger it would pop an EXC_BAD_ACCESS exception on a reference to a UIImage associated with the UITabBarController. I assumed it was just a mistake somewhere. After all, you have to – gulp – manage memory through manual reference counting (Hello, AddRef, never thought I’d see you again. How’s your buddy Release?), so the chance that we were doing one too many releases or one too few retains was not zero. I’d finish the feature set and then hunt it down.

When I finally got around to the hunt-it-down part things weren’t so obvious. As far as I could tell we were doing all the right things to the resources assigned to the tab bar controller. The fact that it only happened after dismissing the application with the home button and then bringing it back had to be meaningful. In fact I had been wondering for some time just what happened when the user hit the home button on an iPhod running iOS 4.x. It was obvious from application behavior that the app wasn’t exiting and restarting. At least, not every time. I started reading up on how iOS manages the application lifecycle, and some lights began going on. Apparently the home button didn’t cause an app to exit, at least not always. If the app didn’t exit it went into a background limbo state, and in that state the operating system might do things. It might, for example, “release unused views.” Oh, really? Lots of lights popped on all at once.

If you were used to writing applications for iOS 3.x (and the guys who started this app were), then you had birth and death to think about, just like any program. When your app started you got a call to application:didFinishLaunching in the app delegate, and when the app exited you got a call to applicationWillTerminate. Typically you allocated your resources at startup, deallocated them in the app delegate’s dealloc method, and life was simple. Enter iOS 4 and “fast task switching.” This facility is a component of Apple’s answer to multi-tasking in iOS 4, the entirety of which includes actual background processing, something I’m not going into now. The piece of it that is relevant here is the switching part. Task switching is what takes place when the user taps the home button, or uses something like Springboard.

If your app is running in the foreground, and the user switches tasks (or a phone call arrives, or anything else happens that would force the app to give up control of the screen) what happens depends on whether the app is compliant with fast task switching. By default all apps built against the 4.0 SDK are. If you want your app to exhibit the old behavior then you have to say so by putting the UIApplicationExitsOnSuspend key in info.plist and setting the value to true. If you do, then the app will simply exit, and applicationWillTerminate gets called as it used to.

If you don’t add that key to info.plist, then the behavior is entirely different. Oliver Drobnik has a great flowchart of the events and accompanying explanation on his Dr. Touch blog. Basically it goes like this: the app starts up fresh and application:didFinishLaunchingWithOptions is called (the replacement for application:didFinishLaunching), followed by applicationDidBecomeActive; the app is now running in foreground and gets switched; applicationWillResignActive is called, followed by applicationDidEnterBackground. After that last call the application is suspended. Unless it is actually doing background processing as alluded to above, nothing is happening, but the image is still in memory. In this stage the OS may, as mentioned, prune the app’s memory usage by releasing invisible graphics objects, and ultimately it may also dump the app from memory if it needs to. If that happens applicationWillTerminate will not be called. Something to keep in mind.

Now what happens going the other way? If the app is suspended in ram and the user taps its icon, then it first gets a call to applicationWillEnterForeground, followed by  applicationDidBecomeActive. In order to avoid the problems we were having you need to at least tear down any invisible/unused views as the app is heading for the background, and then restore these when the app is summoned to the front again, or as needed. So, does applicationWillTerminate ever get called? The answer is pretty much “no.” If you’re building against the 4.0 SDK that method is essentially deprecated. Except… well comments on the Dr. Touch blog indicate that maybe it still does get called if your app is running on earlier devices such as the iPhone 3. I don’ t have one of those to test on, so I’ve decided they don’t exist and my app will never run on them. Solved!

By the way, if you want to see which apps are currently running/suspended on your iOS 4 device double-tap the Home button. The UI will slide up as it does for the Utility app, and a side-scrollable list of app icons will be displayed. When I did this for the first time I was surprised at how many apps were still resident in memory. There is a red badge on each icon that will let you kill the app, and as with a preemptive strike from the OS the app’s applicationWillTerminate method is not called when a user does this. In general I would ignore this list and let the OS manage the app pool, but as a developer it’s nice to know you can force an app out of ram for a restart if you need to.