

iOS & OSX Memory Management: Reference Counting
That thing you kind of understand, but not really
When I first decided I wanted to learn how to make apps, it was back in 2009. iOS 3 was just released and the App Store was then a gold mine for developers that made to-do lists, note taking and farting apps.
Objective-C was my first object orientated language I decided to put all my willpower into learning, and the landscape definitely looked a lot different to what we have now, with the upgrades the language we have seen over the years, as well as the introduction of the ever-so-impressing Swift.
One thing people take for granted these days, especially those people who learnt Objective-C iOS 5 (which was in 2011 mind you) onwards or Swift, is this automagical feature people know as ARC, which stands for Automatic Reference Counting.
What is reference counting?
In computer science, reference counting refers to a technique which lets the application know which objects are still actively in use, because each object is assigned a retain count upon instantiation. Throughout an object’s lifetime, it’s retain count can fluctuate both incrementally and decrementally because other objects may use it by claiming ownership of it, and then releasing it when it has completed it’s task. When an object’s retain count decreases to 0, it will then be deallocated from memory. For example:
- (void)demonstration {
MyClass *foo = [[MyClass alloc] init];
[foo performSomeMethod];
[foo release];
}In the code above, we do three simple things, alloc an object, perform some type of action and then release it. Back in the pre-iOS 5 days, developers had to do this throughout the entire source code for their application. This type of memory management is what was known as Manual Reference Counting or Manual Reference Management.
If you examine the code sample above, your first thought might be that it’s not a big problem because it’s so simple. But as the application and source code grows over time and more developers are brought on to work on the project, the chance for human error increases.
Manual Reference Counting
So what exactly is involved in manual reference counting?
alloc
MyClass *foo = [[MyClass alloc] init];
The code sample above is Objective-C basics 101. To create an object, you must first initialize it. When alloc is called to create an object, the system will give that object a place in memory and set it’s retain count to 1.
release
[foo release];
Calling release on an object will decrement its retain count value by 1. When the object’s retain count reaches 0, the system will remove the object from memory which will in turn free up space for other objects to be created in it’s place if need be.
retain
[foo retain];
When retain is called on an object, it will tell the system to increment it’s retain count value by 1. retain is meant to be used when another object wants to take ownership of foo.
Let’s say two separate objects both have control of foo. If the first object calls release on foo, it’s retain count will be reduced from 2, to 1. This will still allow the second object to use foo without the risk of crashing or creating a dangling pointer.
copy
MyClass *bar = [foo copy];
copy works very similar to retain, meaning that it will create a copy of the original object, with the the exception of the retain count. So if foo had a retain count of 4 when it was copied, the copied object bar would only have a retain count of 1.
autorelease
[foo autorelease];
When the scope of an object exceeds its the bounds of it’s own declaration, autorelease should be called. It tells the system that you don’t want the object released immediately, but instead instructs the system that it should release the object when the autoreleasepool is drained.
autorelease is generally used when an object is created inside a function and then returned to whatever had called it, othewise another example is when an object is instantiated inside of a for-loop, which also happens to house an autoreleasepool.
- (MyClass *)foo {
MyClass *foo = [[MyClass alloc] init];
[foo autorelease];
}autoreleasepool
- (void)example {
for (int i = 0, i < 10, i++) {
@autoreleasepool {
MyClass *foo = [[MyClass alloc] init];
[foo autorelease];
}
}
}As you can see in the code above, we have an instantiation of foo which calls autorelease, inside of a for-loop, inside of an autoreleasepool.
The idea of this is to have all the objects that were instantiated inside of the for-loop during it’s iterations, to automatically be released once the for-loop has completed it’s cycle. When this happens, the autoreleasepool drains itself, leaving everything neat and tidy just as it was.
dealloc
- (void)dealloc {
[foo release];
[bar release];
[fubar release];
[super dealloc];
}The final call from which any object that subclasses itself from NSObject. consider dealloc as a place to tidy up any left over objects that may still have a retain count greater than 0.
Problems with Manual Reference Counting
So now that you have a better understanding of the work that is required when manually managing references, there are two major scenarios that can cause a run time crash from basic human errors.
Dangling Pointer
MyClass *foo = [[MyClass alloc] init];
[foo release];
[foo doSomething];
As I mentioned earlier, when an object’s retain count reaches 0, the system will remove it from memory. That address in memory that was just freed up may either remain empty or another object may takes it’s place.
But one thing to remember is that the *foo pointer still points to that memory address space. So when doSomething is called, it could be asking nil or some other object that took up that recently freed space to perform that action, which will most likely cause a run time crash.
Memory Leak
MyClass *foo = [[MyClass alloc] init];
[foo retain];
[foo retain];
[foo release];
Kind of the opposite to a dangling pointer, a memory leak occurs when the calls for release are less than the calls for retain for an object. If an object’s retain count never reaches 0, the system will be unable to make its resources available for other objects.
It may not seem like such a big deal, having a few objects scattered throughout your application never being freed, but if there are enough objects it can cause the application to run out of memory and resulting in a, cause some type of weird behaviour, burst into flames and eventually crash. This is why we have the dealloc cleanup within each object.
Introducing Automatic Reference Counting
Session 323 — iOS, OS X
The year is 2011, its a pleasant June morning in San Francisco and everyone is seated within the Hall-H for the annual WWDC meeting. We had Phil Schiller demoing the new Mail.app, Scott Forstall showing off Game Center’s beautiful UI (that green, ugh!), unfortunately it was also Steve Jobs’ final keynote 😔. A lot happened in the keynote except for the announcement of ARC, oh how what a surprise this was going to be for developers. Later in the week, attendees were treated to Session 323 for iOS and OSX, their lives would never be the same.
Automagical
To this very day, reference counting is still a thing and it is still happening within all our iOS and OSX applications, the only thing is that we don’t have to manage it anymore because the compiler does it for us.
Yeah, totally being serious here. All the stuff I presented above, the compiler inserts it for us at compile time, retain, release, copy, autorelease and autoreleasepool. If you scroll back up and look at all the code I demonstrated to you, imagine all five of those calls were commented out, that’s pretty much ARC in a nutshell. So go forth, young padwans, be free, write apps, not manage memory! Except for retain cycles, but thats for the next post.
I’ve uploaded a sample project to GitHub for you to have a look at the memory management syntax in Xcode. It’s just a small OSX console application project with ARC turned off. Example function calls are commented out, if you want to see them run, simply un-uncomment them, build and run. Enjoy, yo!