If you’re new to iOS programming and Objective-C, one of the first things you’ll need is a firm grasp on how memory management works. Consequently, most iOS programming books written prior to iOS 5 spent time teaching you how to use retain, release, and autorelease. Even though Automatic Reference Counting (ARC) has made things a lot easier since Xcode 4.2, it’s still important to have a basic understanding of what’s happening under the covers so you’ll know where to look when your code breaks. The key point is this:
ARC does not change how memory management works - it only changes what you do as a programmer.
Happily, this means a lot less work! In a sense, memory management has gone from being a completely manual effort to one that is now fairly automatic.
As quick review, in iOS we work with Cocoa’s basic set of memory management rules, which can be summarized as follows:
- If you need to keep an object around, you must retain it unless you know it’s already been retained for you.
- When you want to get rid of an object that you own, you need to release it, unless you know it’s already been released for you. (e.g. using autorelease).
A typical retain/release cycle, which you will see in posts on StackOverflow, in older iOS books, and in online tutorials looks like this:
// First you create an object, which you then own ("retain")
// Retain count = 1
Customer *newCustomer = [[Customer alloc] init];
// Next, you do something with the new object, such as add it to an
// array, which also now retains it. Retain count = 2
// When finished with the object, you relinquish (release) ownership.
// Retain count = 1
// At some point customerArray is also released, releasing all of the
// objects it contains. Retain count = 0, and the original object is
// finally deallocated
These rules seem simple, but they can be devilishly hard to consistently follow, especially in larger applications.
Starting with iOS 5, Apple included support for Automatic Reference Counting (ARC) in the compiler, which is enabled by default when you create a new project. Where in the past you would have to worry about calling retain, release, and autorelease in all the right places, under ARC, the compiler now does all of this for you. So the first key take-away is this:
Under ARC, you no longer have to hand code memory management.
In fact, Xcode will flag an error if you try.
This frees you to concentrate on just one thing when thinking about the lifecycle of an object: ownership - do I need this object or not?
Under ARC, as long as there is a variable pointing to an object, the object stays alive in memory without any action on your part. This brings us to the other key take-away: pointer types.
There are two types of pointers: strong and weak. You can easily remember them like this:
- Strong pointers are “owners” that keep objects alive. (strong = live)
- Weak pointers are not owners and do not keep objects alive. (weak = die)
So if we set up the following property in our header (.h) file as follows:
@property (nonatomic, strong) NSString *companyName;
And then in our implementation file (.m) we point it to a string object,
self.companyName = @"Acme Explosives";
The picture in memory looks like Figure A.
Let’s now assign the same object to the text property of a UILabel in our user interface, which is also a strong pointer (note: by default, variables and properties are created as “strong” types),
self.label.text = self.companyName;
The picture in memory now looks like Figure B.
If we later decide to set self.companyName to point to some other object, @”Acme Explosives” remains alive because self.label.text still points to it, as shown in Figure C.
At some point later however, self.label.text may also point to a different string object. At this point, @”Acme Explosives” has no more owners and will be deallocated. (Figure D)
Now let’s add a weak pointer type into the picture. Remember that weak properties and variables can point to objects, but cannot own them. We’ll point our UILabel at a new string object and define a weak property that points there also:
@property (nonatomic, weak) NSString *wylieCoyote;
The results in Figure E are almost identical to Figure B, except that now one of the pointers is a weak reference.
If we now point self.label.text elsewhere, @”Road Runner” no longer has any owners and is deallocated, as shown in Figure F. This also results in wylieCoyote being set to nil, which turns out to be a very good thing, because it prevents wylieCoyote from becoming a so-called “Zombie”.
Zombies are “dangling” pointers that reference deallocated objects, and in the days of manual memory management, they were a notorious source of crashes. Referencing deallocated objects typically results in an EXC_BAD_ACCESS error at run time, whereas referencing a nil object is completely harmless.
Using ARC is very simple, and you should use it whether you’re new to iOS programming or a seasoned veteran. If you’re just starting out, you’ll want to be sure to understand the difference between strong and weak pointers and learn when it makes sense to use each type.
If you’re a battle-hardened veteran, using ARC means you simply don’t need to call retain, release, and autorelease any longer. ARC will not impact the performance of your app because it is simply inserting retains and release into your code at compile time. In some cases, it can even optimize performance. And best of all, the bad old days of causing a memory leak because you missed a retain are over.