Understanding memory management in the Cocoa Touch framework is one of the first major roadblocks for newcomers. Unlike Objective-C on the Mac, Objective-C on the iPhone has no garbage collector. Thus, it is your responsibility to clean up after yourself.
This book assumes you are coming from a C background, so the words “pointer,” “allocate,” and “deallocate” shouldn’t scare you. If your memory is a little fuzzy, here’s a review. The iPhone has a limited amount of random access memory. Random access memory (RAM) is much faster to write to and read from than a hard drive, so when an application is executing, all of the memory it consumes is taken from RAM. When an operating system like iPhone OS launches your application, it reserves a heaping pile of the system’s unused RAM for your application. Not-so-coincidentally, the memory your application has to work with is called the heap. The heap is your application’s playground; it can do whatever it wants to it, and it won’t affect the rest of the OS or any other applications.
When your application creates an instance of a class, it goes to the giant heap of memory it was given and takes a little scoop. Since you typically create objects during the course of your application’s execution, you start using more and more of the heap. Most objects are not permanent, and when an object is no longer needed, the memory it was consuming should be returned to the heap. This way, it can be reused for another object created later.
There are two major problems in managing memory:
In the C programming language, you have to explicitly ask the heap for a certain number of bytes. This is called allocation. It is the first stage of the heap life cycle shown in Figure 3.1. To do this, you use a function like malloc
. If you want 100 bytes from that heap, you do something like this:
You then have 100 bytes with which you can perform some task like writing a string to it and then printing that string (which would require reading from those bytes). The location of the first of those 100 bytes is stored in the pointer buffer
. You access the 100 bytes by using this pointer.
When you don’t want to use those bytes anymore, you have to give them back to the heap by using the free
function. This is called deallocation.
By calling free
, those 100 bytes (starting at the address stored in buffer
) are returned to the heap. If another malloc
function is executed, any of these 100 bytes are fair game to be returned. Those bytes could be divvied up into smaller sections, or they could become part of a larger allocation. Because you don’t know what will happen with those bytes when they are returned to the heap, it isn’t safe to access them through the buffer
pointer anymore.
Even though at the base level an object is bytes allocated from the heap, you never explicitly call malloc
or free
with objects.
Every class knows how many bytes of memory it needs to allocate for an instance. When you create an instance of a class by sending it the alloc
message, the correct number of bytes is allocated from the heap. Like with malloc
, you are returned a pointer to this memory (Figure 3.2). However, when using Objective-C, we think in terms of objects rather than raw memory. While our pointers are still pointing to a spot in memory, we don’t need to know the details of that memory; we just know we have an object.
Of course, once you allocate memory from the heap, you need a way to return that memory back to the heap. Every object implements the method dealloc
. When an object receives this message, it returns its memory back to the heap.
So, malloc
is replaced with the class method alloc
, and the function free
is replaced with the instance method dealloc
. However, you never explicitly send a dealloc
message to an object; an object is responsible for sending dealloc
to itself. That begs the question: if an object is in charge of destroying itself, how can it know if other objects are relying on its existence? This is where reference counting comes into play.
In the Cocoa Touch framework, Apple has adopted manual reference counting to manage memory and avoid premature deallocation and memory leaks.
To understand reference counting, imagine a puppy. When the puppy is born, it has an owner. That owner later gets married, and the new spouse also becomes an owner of that dog. The dog is alive because they feed it. Later on, the couple gives the dog away. The new owner of the dog decides he doesn’t like the dog and lets it know by kicking it out of the house. Having no owner, the dog runs away and, after a series of unfortunate events, ends up in doggy heaven.
What is the moral of this story? As long as the dog had an owner to care for it, it was fine. When it no longer had an owner, it ran away and ceased to exist. This is how reference counting works. When an object is created, it has an owner. Throughout its existence, it can have different owners, and it can have more than one owner at a time. When it has zero owners, it deallocates itself and goes to instance heaven.
An object never knows who its owners are. It only knows its retain count (Figure 3.3).
When an object is created — and therefore has one owner — its retain count is set to 1. When an object gains an owner, its retain count is incremented. When an object loses an owner, its retain count is decremented. When that retain count reaches 0, the object sends itself the message dealloc
, which returns all of the memory it occupied to the heap.
Imagine how you would write the code to implement this scheme yourself:
Simple, right? Now let’s consider how retain counts work between objects. If object A creates object B (through alloc
and init
), A must send B the message release
at some point in the future. Releasing B doesn’t necessarily deallocate it; it is left to B to decide if it should be deallocated. (If B has another owner, it won’t destroy itself.)
If some other object C wants to keep B around, C becomes an owner of B by sending it the message retain
. What reason does C have to keep B around? C wants to send B messages.
Let’s imagine you have a grocery list. You created it, so you own it. Later, you give that grocery list to your friend to do the shopping. You don’t need to keep the grocery list anymore, so you release it. Your friend is smart, so he retained the list as soon as he was given it. Therefore, the grocery list will still exist whenever he needs it, and your friend is now the sole owner of the list.
Here is your code:
Here is your friend’s code:
Retain counts can still go wrong in the two classic ways: leaks and premature deallocation. First, you could give the grocery list to your friend who retains it, but you don’t release it. Your friend finishes the shopping and releases the list. You have forgotten where it is, but because you never released it, it still exists. Nobody has this grocery list anymore, but it still exists because its retain count is greater than 0. This is a leak.
Think of the grocery list as an NSString
. You have a pointer to this NSString
in the method where you created it. If you leave the scope of the method without releasing the NSString
, you’ll lose the pointer along with the ability to release the NSString
later. Even if every other object releases the NSString
, it will never be deallocated.
Consider the other way this process can go wrong — premature deallocation. You create a grocery list and give it to a friend, who doesn’t retain it. When you release it (thinking it was safe with your friend), it is deallocated because you were its only owner. When your friend attempts to use the list, he can’t find it because it doesn’t exist anymore.
When an object attempts to access another object that no longer exists, your application accesses bad memory, starts to fail, and eventually (although sooner is better than later for debugging) crashes.
If an object retains another object, that other object is guaranteed to exist. So correct use of retain counts avoids premature deallocation. Now let’s look more closely at memory leaks.
You already know that an object is responsible for returning its own bytes to the heap and that an object will do that when it has no owners. What happens when you want to create an object to give away, not to own? You own it by virtue of creating it, but you don’t have any use for it.
Let’s make this idea more concrete with an example from the RandomPossessions tool you wrote last chapter. In the Possession
class, you implemented a convenience method called randomPossession
that return an instance of Possession
with random parameters. The owner of this instance is the class Possession
(because the object was created inside of a Possession
class method), but Possession
is only creating it because another object wants it. The pointer to the Possession
instance is lost when the scope of randomPossession
runs out, but the object still has a retain count of 1.
Now, in your main
function, you could release the instance returned to you by this method. But, you didn’t allocate the random possession in the main
function. Therefore, releasing the memory isn’t main
’s responsibility. Since the alloc
message was sent to the Possession
class inside randomPossession
’s implementation, it is randomPossession
’s responsibility to release the memory. But looking at the following block of code, where could you safely release it?
How can you avoid this memory leak? You need some way of saying “Don’t release this object yet, but I don’t want to be an owner of it anymore.” Fortunately, you can mark an object for future release by sending it the message autorelease
. When an object is sent autorelease
, it is not immediately released; instead, it is added to an instance of the NSAutoreleasePool
. This NSAutoreleasePool
keeps track of all the objects that have been autoreleased. Periodically, the autorelease pool is drained; it sends the message release
to the objects in the pool and then removes them.
An object marked for autorelease after its creation has two possible destinies: it can either continue its death march to deallocation or another object can retain it. If another object retains it, its retain count is now 2. (It is owned by the retaining object, and it has not yet been sent release
by the autorelease pool.) Sometime in the future, that autorelease pool will release it, which will set its retain count back to 1. The return value for autorelease
is the instance that is sent the message, so you can method chain autorelease
.
// Because autorelease returns the object being autoreleased, we can do this:
NSObject *x = [[[NSObject alloc] init] autorelease];
Sometimes the idea of “the object will be released some time in the future” confuses developers. When an iPhone application is running, there is a run loop that is continually cycling. This run loop checks for events (like a touch or a timer firing) and then processes that event by calling the methods you have written in your classes. Whenever an event occurs, it breaks from that loop and starts executing your code. When your code is finished executing, the application returns to the loop. At the end of the loop, all autoreleased objects are sent the message release
, as shown in Figure 3.4. So, while you are executing a method, which may call other methods, you can safely assume that an autoreleased object will not be released.
Accessors are methods that get and set instance variables. Getter methods don’t require any additional memory management:
Setters, however, need to take care to properly retain new values and release old ones.
Notice that if pet
hasn’t been set, it is nil
, and [pet release]
would have no effect.
It is important to retain the new value before releasing the old one. Why? What if pet
and d
are pointers to the same object? What if that object has a retain count of 1? If you release it before you retain it, the retain count goes to 0, and the object is deallocated.
Here is the same thing in another style:
Once again, properties come to the rescue. If you use properties, all of the memory management code for your accessors is written for you when you synthesize the property. To have the compiler generate an accessor that properly releases and retains for you, you can use the retain
attribute when declaring your properties in a header file:
@property (nonatomic, retain) Dog *pet;
Then, in the implementation file, synthesize the method:
@synthesize pet;
Let’s make a few rules from these ideas:
• If you send the message alloc
to a class, the instance returned has a retain count of 1, and you are responsible for releasing it.
• If you send the message copy
(or mutableCopy
) to an instance, the instance returned has a retain count of 1, and you are responsible for releasing it (just as if you had allocated it).
• Assume that an object created through any other means (like a convenience method) has a retain count of 1 and is marked for autorelease.
• If an object wants to keep another object around (and the keeper didn’t allocate it), it must send the wanted object the message retain
.
• If an object no longer wants to keep another object around, it sends that object the message release
.
There is one exception to the rules: in any method that starts with new
, the object returned should be assumed to not be autoreleased.
Now that you have the theory and some rules, you can implement better memory management in RandomPossessions. Open the RandomPossessions.xcodeproj
file that you created in the last chapter. There are four memory management problems to fix in this project.
The first is found in the main
function of RandomPossessions.m
where you created an instance of NSMutableArray
named items
. You know two things about this instance: its owner is the main
function and it has a retain count of one. It is then main
’s responsibility to send this instance the message release
when it no longer needs it. The last time you reference items
in this function is when you print out all of its entries, so you can release it after that:
The object pointed to by items
decrements its retain count when this line of code is executed. In this case, that object is deallocated because main
was the only owner. If another object had retained items
, it wouldn’t have been deallocated.
There is one more detail to take care of. The instance of NSMutableArray
that items
pointed to is now gone. However, items
is still storing the address that was the instance’s location in memory. It is much safer to set the value of items
to nil
. Then any messages mistakenly sent to items
will have no effect.
[items release];
items = nil;
The ordering of those two statements is important. Ordering them this way says, “Send the object release, and then clear my pointer to it.” What would happen if you swapped the order of these statements? It would be the same thing as saying, “Set my pointer to this object to nil
and then send the message release
to.... Oh, no! I don’t know where that object went!” You would leak the object: it wasn’t released before you erased your pointer to it.
The second memory problem occurs when you create an instance of NSMutableArray
and fill it with instances of Possession
returned from the randomPossession
convenience method:
The implementation for randomPossession
returns an instance of type Possession
that it created by sending the message alloc
. This object is owned by this class method and therefore has a retain count of 1.
When you add a Possession
instance to an NSMutableArray
, the array becomes an owner of that object, so its retain count is increased to 2. After randomPossession
finishes executing, however, it loses its pointer to the Possession
it created. The Possession
instance still has two owners, but only one still has a pointer to it (items
). Memory leak!
This is a perfect opportunity to use autorelease
. The method randomPossession
should send autorelease
to an instance it creates and relinquish its ownership of that instance. The object will still exist temporarily and be retained when it is added to the NSMutableArray
. The instance of NSMutableArray
will then be the sole owner of this new Possession
. In effect, you have transferred ownership of the instance from randomPossession
to items
. When the array deallocates itself and releases the objects it contains, each object will have a retain count of 0 and will deallocate itself. Memory leak solved.
Now fix the leak in the randomPossession
method in Possession.m
.
When working with an instance of NSMutableArray
, three rules apply to object ownership:
• When an object is added to an NSMutableArray
, that object gets sent the message retain
; the array becomes an owner of that object and has a pointer to it.
• When an object is removed from an NSMutableArray
, that object gets sent the message release
; the array relinquishes ownership of that object and no longer has a pointer to it.
• When an NSMutableArray
is deallocated, it sends the message release
to all of its entries as shown in Figure 3.5.
The third memory problem in RandomPossessions is in the description
method that Possession
implements. This method creates and returns an instance of NSString
that needs to be autoreleased.
You can make this even simpler by using a convenience method. NSString
(as well as many other classes in the iPhone SDK) includes convenience methods that return autoreleased objects. Update description
to use the convenience method stringWithFormat:
to ensure that the NSString
instance that description
creates will be autoreleased.
The final memory problem has to do with the instance variables within Possession
objects.
When the retain count of a Possession
instance hits zero, it will send itself the message dealloc
. The dealloc
method of Possession
has been implemented by its superclass, NSObject
, but NSObject
knows nothing about the instance variables added to Possession
. So you must override dealloc
in Possession.m
to release any instance variables that have been retained.
Always call the superclass implementation of dealloc
at the end of the method. When an object is deallocated, it should release all of its own instance variables first. Then, it should go up its class hierarchy and release any instance variables of its superclass. In the end, the implementation of dealloc
in NSObject
will return the object’s memory to the heap.
Now let’s check your understanding of memory management concepts by looking more closely at instance variables and memory management.
Why send release
to instance variables and not dealloc
?
One object should never send dealloc
to another. Always use release
and let the object check its own retain count and decide whether to send itself dealloc
.
Why do you need to release these instance variables in the first place? Where are the calls to alloc
, retain
, or copy
that make an instance of Possession
an owner of these objects?
Let’s start with the instance variable dateCreated
. Because it is allocated in the designated initializer for Possession
, that instance of Possession
becomes an owner and needs to release the object pointed to by dateCreated
according to the first of the retain count rules described on page 52.
To figure out the other two instance variables, possessionName
and serialNumber
, you have to go back to their property declarations in Possession.h
.
@property (nonatomic, copy) NSString *possessionName;
@property (nonatomic, copy) NSString *serialNumber;
Both of the properties associated with these instance variables have the copy
attribute. When the message setPossessionName:
is sent to an instance of Possession
, the incoming parameter is sent the message copy
. The instance variable possessionName
is then set to point at that copied instance. If you wrote the code for setSerialNumber:
instead of using @synthesize
, it would look something like this:
The second retain count rule states that, if an object copies something, the object becomes an owner of that thing. Therefore, the owning object needs to release the copied object in its dealloc
method. The same would hold true of these instance variables if their property attribute was retain
(but not if the attribute were assign
, which is a simple pointer assignment).
Strings come in two flavors: NSString
and NSMutableString
. Because an NSString
can never be changed, there is seldom a need to copy it. Thus, in the case of NSString
(and most other immutable objects), the copy method looks like this:
This approach prevents unnecessary copying. For example, the code above is basically equivalent to this:
Note, however, that this code is not exactly equivalent. Because of some underlying implementation details, both NSString
and NSMutableString
might return YES if asked whether they are of type NSMutableString
.
Sometimes in this book, we will show you example code that does not exactly match the implementation in the SDK. We do this to give you a better understanding of the concepts being discussed. We’re not lying to you; we’re just sparing you some of the details until you’re more comfortable with the concepts. Once you’re there, you can divine all the details from the documentation. (In fact, we already did this when we gave an example of implementing retain
and release
earlier in this chapter. The implementations of these methods are actually much dirtier.)
Congratulations! You’ve implemented retain counts and fixed four memory leaks. Your RandomPossessions application now manages its memory like a champ!
Keep this code around because you are going to use it in later chapters.