This is the second article on our series about ARC. In the previous one we introduced the basic design concepts behind it, now we will explain a bit in detail which precise methods and features are deprecated from manual memory management and the new keywords and statements we will use with it.
Enabling ARC
As any other compiler feature, ARC is enabled by setting the parameter -fobjc-arc in the compiler settings. If you use the refactoring tool in an existing project, this setting will be added automatically.
Refactoring tool
The refactoring tool removes all the calls to existing methods, converts the lifetime qualifiers for properties and variables, replaces autorelease pool with the new @autorelease statement and every other detail. It worked flawlessly in our tests for most projects, saved some external libraries, so please don’t hesitate to give it a try. In case some code couldn’t be automatically converted, the manual procedure to get it fixed is usually straightforward.
Deprecated methods
As soon as you enable ARC, using any of the following methods will result in a compiler error (this includes the @selector() statements): retain, release, autorelease, retainCount and explicit dealloc invocations (including [super dealloc]). Although not necessary most of the times, you can still implement dealloc to perform other cleanup operations (i.e. canceling pending networks operations) but invoking the super class is not required or even allowed.
Autorelease pools
NSAutoreleasePool objects are not accesible anymore directly from the code. Instead, the compiler provides the new statement @autorelease which performs the same task you used to do before with them. The most straightforward example is main.m:
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
becomes
int main(int argc, char *argv[])
{
@autorelease {
return UIApplicationMain(argc, argv, nil, nil);
}
}
Properties lifetime qualifiers
In the previous memory management model, we explicitly define whether a property retain count should be increased or decreased, or just be untouched when a property is accessed via the dot operator – these are the well-known retain and assign qualifiers. Roughly speaking we just have to replace them by the new strong and weak types. Let me extend a little bit more on what this means.
Lets say we have an existing property defined as:
@property (retain) NSString *text;
The property is synthesized in the @implementation section as usual, instructing the compiler to create the setter and getter methods. Although you never see the code, a typical setter method created by the compiler would look like this:
- (void) setText:(NSString *) _text
{
[text release];
text = [_text retain];
}
thus releasing the existing ivar (if it is nil, nothing will happen of course) and increasing the retain count on the passed object. In addition to that, to avoid memory leaks you should instruct the object containing the property to release the previously assigned object at the end of its lifetime, usually like this:
- (void) dealloc
{
[text release];
[super dealloc]
}
There are more way so deal with this (I’m talking about setting the property to nil for example) but those concerns are out of this discussion. When jumping into ARC, it is up to the compiler to deal with these concerns and as a developer you just focus on the relationships between the objects in your application. In this example, you just have to specify that the property is strong, meaning that the assigned object should exist at least during the lifetime of the object containing the property. The code will just be:
@property (strong) NSString *text;
Plus of course the @synthesize directive that creates as before the setters and getters. Well, that by itself will save up not only development time but also debugging problems and leaks. What about the assign qualifiers? Same applies here, code previously containing properties marked as assign will just become weak, meaning you don’t expect for the assigned object to stay in memory during the lifetime of the parent object.
Now let’s go for a slightly more complicated case, please take a look to this code:
self.text = [[NSString alloc] init];
As of previously memory model, this code will produce a leak. Why? The string is created with a retain count of 1, assigning it via the dot operator to the property previously defined with the retain qualifier also increases it becoming 2. When the lifetime of the parent object ends, the string retain count is decreased by 1, but still the object retain count doesn’t reach zero so dealloc is not invoking and we have an unreleased object which lacks ownership (no one can claim or use it, as the reference is lost when the parent object disappears from memory) thus creating a leak. If you happen you have many of those, you’re memory will be exhausted soon and the application will crash.
The way you usually fix this is by either marking the object to be released in the next autorelease pool cycle or just releasing it after the assignment.
self.text = [[[NSString alloc] init] autorelease];
or
NSString *string = [[NSString alloc] init]; self.text = string; [string release];
ARC is smart enough to insert those by itself by inspecting the lifetime of the object through code analysis. That’s the kind of magic the compiler implements and the nuisances it ’releases’ the developer to deal with. Bye bye leaks!
Variables lifetime qualifiers
We won’t go deep into this, but besides properties and ivars (usually residing on the heap) you will also have variables in the stack frame. The new type qualifiers are used the same way to do with static or const, but in most cases you just want for them to be strong, it’s the case by default. The new qualifiers are __strong, __weak, __unsafe_unretained and __autoreleasing. Lets talk about them a little bit.
The __strong qualifier, the one used by omission instructs the compiler the keep the object in memory during the lifetime of the stack frame, that’s usually the block of code marked by braces. __weak variables are a little bit trickers, as the compiler will get rid of the object in memory as soon as possible even during the stack frame existence. Please note that it also will set it to nil, to avoid crashed on sending messages to dangling pointers.
Similar to __weak is __unsafe_unretained – it will deallocate the object as soon as possible, but it won’t assigned nil to the variable (thus the unsafe part I assume). The exact use case for it varies from function to function but you will find a number of them when interacting with static variables defined somewhere else or Core Foundation toll-free bridge objects.
Finally __autoreleasing will mark the object to be autoreleased when the stack frame is released, this is what we should use for factory functions returning argument references (NSError **error like) and you want them autoreleased on return from the function. Please note that this is not necessary when just returning an object you just creating.
In a nutshell, you won’t have to use these qualifiers in most occasions, as the default one if sufficient for most use cases. Nevertheless be aware the specific behavior or use cases should take them in account if you want your code to work the way you want to.
Other considerations and coexistence
There are some other restrictions you must enforce in order to make you code coexists with the old scheme, name you can’t name properties starting with new, as this is used internally by the compiler. Additionally, if you use Core Foundation or any other C/C++ based libraries, the following rules apply:
CFRetainandCFReleasemust be still used- Using object pointers in C structures is not allowed anymore
- No more casting between
idand(void *)types
Check the documentation!
Apple currently provides some of the most comprehensive documentation set for their software products. Although ARC is a new product you will find some documentation describing the new features and possibilities that it opens to developers. Please check this link [developer.apple.com] to get an introduction on ARC. This other article [developer.apple.com] on memory management shows a little bit more what’s happening internally. Both of them are a really nice read that gives a good insight on most topics regarding memory and iOS.