How to correctly use Automatic Reference Counting in your applications
Objective-C is a simple, elegant, and easy to use language, and it gives developers the opportunity to write great applications with it. Starting with iOS 5, a new very cool and powerful technology was introduced – Automatic Reference Counting. Before that, to manage the life-cycle of objects, we had to use manual objects counting, where we have to take care of allocating and deallocating memory for objects automatically, and to avoid writing code that could lead to memory leaks or application crashing situations.
To do everything manually was always an undertaking task, especially for new programmers on the Apple’s platform, and memory management has been a common problem for many newbie programmers before. Fortunately, Automatic Reference Counting, or shortly ARC, eliminates the manual and error-prone steps involved in reference counted objects. This way we can concentrate more to designing objects relationships and the functionality of our application.
Whether you are new or already familiar to Objective-C, there is plenty to learn about this powerful technology, and the more you learn about it, you’ll be able to write simpler, more robust and maintainable code.
What exactly is ARC?
Automatic Reference Counting implements automatic reference counting for Objective-C objects and blocks, freeing the programmer from the need to explicitly insert retains and releases.
If I don’t take care of deallocating memory explicitly, who does it for me?
LLVM Compiler 3 does the memory management for us. It insert all the necessary lines of code to release the memory at the corresponding points in our programs, and more importantly, it does these all things at compile-time, not at run-time.
Simply said, with ARC you have to forget about retain, release and autorelease. Also, the compiler gives you errors, and not warnings whenever you send any of these messages to your objects.
If you add a Stack class to your Project (which is using ARC), and write the code as below:
After sending the release message to the _array object, you will notice the red exclamation sign on the left of the last line inside the pushint method:
The above line will be added by the compiler at compile-time, and because of that, we are not allowed to write that when using ARC.
We can also use convenience class methods:
In the example above, nothing changes if we are using ARC or not.
But if we are using convenience methods with instance variables, like in the following case:
In this example, we’ll get surprised how our application crashes if we are doing it under manual memory management. The problem appears because the array created inside the if statement is autoreleased, and because it’s not being retained, it will be deallocated just after the init method is finishing execution. Any other lines of code we’ll try to execute thereafter that involve manipulating with that instance variable will make our app crash. To fix that, we can replace the line:
with the following line:
Now we own the array and it won’t be deallocated. The app will not crash anymore (at least because of this line of code), but this made a memory leak to appear inside our program. To make everything work as supposed, we need to override the dealloc method:
The example above illustrate how easily we can cause memory leaks when doing memory management manually, if we just forget to release some objects inside the dealloc method. Also we can get in trouble trying to use objects that have already been deallocated, but we are not aware of it.
Under ARC, we don’t have this head-ache of releasing the allocated objects, because the compiler does everything on our behalf: keeps track of all the instance variables, and releases them accordingly. And we do not have to override the dealloc method anymore, because it is done automatically for us. Basically this code is inserted in our program at compile-time:
Notice that the LLVM Compiler also calls the dealloc method of the superclass, and it will send the release messages to all the other instance variables if there are any. Actually, when using ARC, if we can override the dealloc method, but we are not allowed to invoke [super dealloc]; or [_someObject release];. It simply won’t compile.
With ARC, we may even use the convenience class method (unlike when we compiled under manual reference counting which made the program crash):
And it works perfectly, just like if we would write:
If we add a pop method to the Stack class like this:
Under manual reference counting, this method will not work well, and may actually crash. This may happen because that lastObject that we want to return may only be retained by the array itself. In that case the when the object is removed from the array, the retain count is decremented, and the object is deallocated. So, this method is possible to return a deallocated object, which will make us unhappy at some point.
We may fix a little the problem by writing this code:
But another problem appears again: we will retain an retained object, which will cause a memory leak, and also violates the naming conventions used in Objective-C. To make things go well, we have to autorelease the x object when we return it, as follows:
This is how the problem is solved without ARC. When using ARC, we write robust code like this:
And it works perfectly and error-free. What happens is that the x object will hold a strong reference to the lastObject of the array, and the removeLastObject message sent to the array object simply removes the strong reference of the array to that specific object, but the object itself is still present in memory and good to use. The other object in our program that may get initialized by sending the pop message to the object of the Stack class also holds a strong reference to that object, and when it gets destroyed – that object that persisted in memory will get destroyed as well.