Memory Management with Reference Counting
As we have mentioned in our previous post, manual reference counting is used to manage memory and avoid premature deallocation and memory leaks.
Retain Counts
An object never knows who its owners are. It only knows its retain count – i.e. how many times it has been retained in memory.
When an object is first created, it has one owner and hence 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 the retain count reaches 0, the object sends itself the message dealloc, which returns all of the memory it occupied to the heap.
If we are to write our own code to represent this logic, it would look like:
- (id)retain
{
retainCount++;
return self;
}
- (void)release
{
retainCount--;
if (retainCount == 0)
[self dealloc];
}
So, how does 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. This is because if B has another owner, it will not destroy itself.
If another object C wants to keep B around, C can become the owner of B by sending it the message retain. What reason does C have to keep B around? C wants to send B messages.
Imagine if a Store (A) in a game has a list of weapons for sale, of which one is a Sword (B). The Store (A) is where these weapons are created, so the Store (A) is the owner of the Sword (B). Later on, the user’s game character (C) buy over the Sword (B) so the Store (A) releases it. The game character (C) automatically retains the Sword (B) as soon as it receives it. The Sword (B) will exist whenever the character (C) needs it and the character (C) is now the sole owner of the Sword (B).
Here is the code representing this logic:
- (void) createAndGiveAwaySword
{
// Create the sword
Sword *b = [ [ Sword alloc ] init ];
// (The retain count of b is 1 at this point)
// A character takes the sword from the store and retains it
[ hero takeSword:b];
// (The retain count of b is now 2)
// The store gives up ownership
[ b release ];
// (The retain count now drops back to 1)
// This method's responsibility for b is now finished.
}
Corresponding, the code representing how the character takes ownership of the sword is:
- (void)takeSword:(Sword *)b
{
// Take ownership
[ b retain ];
// assign the object to a pointer
mySword = b
}
This is how retain counts or reference counting work in iOS.
Unfortunately, retain counts can still go wrong due to leaks or premature deallocation.
Memory Leaks
For example, we can give the sword to a character who retains it but we forget to let our store release it. The character continues on its adventure in the game and at some point, decides to ditch the sword and releases the memory appropriately. Unfortunately, because we have forgotten to tell our store to release it earlier when handing over the sword to the character, the retain count is still greater than 0. This is a typical scenario resulting in a memory leak.
Premature Deallocation
The other problem is premature deallocation. Continuing on our example above, if our store created the sword and give it to a character who somehow does not retain it because we did not state “[ b retain ]” in our code and we now release the sword thinking that the character is retaining it, the sword is in fact deallocated because the store was its only owner. When our character attempts the sword, the character can not find it because it has already been deallocated and no longer exists!
When an object attempts to access another object which no longer exists, the app accesses bad memory, begins to fail and will eventually crash.
If an object retains another object, then, the other object is guaranteed to exist. So the correct use of retain counts avoids premature deallocation.
In our next post, we will talk about how to use autorelease in Objective-C to create an object to give away without needing to own it.