The Jungle, Part 4: Automatic Reference Counting


So far, we have been manually doing our memory management according to the basic rules established a long time ago. This method is still fine and is still the standard way to do it, but at WWDC 2011 Apple unveiled the revolutionary ARC technology. ARC stands for Automatic Reference Counting which does exactly what it sounds like—it automates the reference counting steps for you. It follows the exact same rules as the old memory management implementations, just automated for you. Rather than doing a runtime cleanup, as garbage collection does (a process which slows the running of your program at random intervals), the compiler looks at your code and automatically inserts retain, release, and autorelease calls as necessary. In this way, the performance of your application isn’t impacted in the least but it could save you a lot of work—after all, computers were designed to do menial labor like that.

In this post we’ll convert our existing project to ARC. Once we’re there, little work remains in memory management. Basically, ARC frees us from thinking about memory management at all, so upon finishing the conversion process we won’t be dealing with memory management anymore. Let’s get started.

Converting to ARC

Open our project and go to Edit > Refactor > Convert to Objective-C ARC…. You’ll get a sheet that says “Select Targets to Convert”; select the only option the list which should be called “SDK Demo.app (SDK Demo)”. Then click Precheck. Xcode will build your project then present a new sheet with an introduction.

ARC Conversion Intro Screen

ARC Conversion Intro Screen

Click Next and it’ll say “Generating Preview” for a few moments. The sheet will then expand, providing you with a two column view of your code.

Revisions Editor

Revisions Editor

To the very left you’ll see a list of files. In the main content, the left pane is the new code; the right pane is what you currently have. Changed lines are shaded in blue with a darker blue outline; changed code is highlighted in a salmon-esque color (by default). You can go through the list of files on the left (there should be five) and see the changes that ARC will do for you. You’ll notice that in the properties, retain is replaced by the new keyword strong; in the implementation files dealloc methods are stripped away, as are retain, release, and autorelease calls. The latter methods are now obsolete and you can’t actually call them anymore. Dealloc is not subject to the same treatment, but unless you’re doing some custom cleanup that goes beyond freeing memory, you shouldn’t have your own dealloc. Review the changes, and click Save in the lower-right.

Now Build & Run the app and when it comes in the Simulator you’ll notice that everything works just as before. …And, that’s it! Converting an app to ARC is just that easy. Note though, that in some cases the compiler may encounter cases where it can’t directly convert your code, in which case it’ll issue an error which you will have to address before you can run your app. This is often the case with files you might receive as part of someone else’s code library. In those cases, it is usually best to allow the original developer to support ARC or not as necessary. For the time being though, you can turn off ARC on a per-file basis to suppress those errors and run the code with the existing memory management code.

Selectively Turning Off ARC

To do so, select the Top Level project in the File Navigator, and select the name of your Target in the pane immediately to the right. Then in the main content pane, select Build Phases from the top, and expand the disclosure triangle next to “Compile Sources”.

Disable ARC per file by going to Build Phases > Compile Sources

Disable ARC per-file here

Select one or more files that you wish to exclude from ARC, and then hit Return on the keyboard. A little text field will appear. Inside that field, type

-fno-objc-arc

Which is a compiler flag to turn off ARC for those files. Hit Done and you’ll see the flag appear next to the files you’ve selected.

Compatibility

ARC code is fully supported in iOS 5, as well as OS X 10.7 Lion. In addition, most of ARC (exceptions detailed below) is supported in iOS 4 and OS X 10.6 Snow Leopard.

Language Changes

Converting a simple app like ours to ARC is not difficult. But ARC adds and modifies more of the Objective-C language, and these changes are worth talking about.

Autorelease Pools

Before ARC, you created autorelease pools like any other object and drained them when you’re done. However, because autorelease is no longer supported by the language, the old method wouldn’t work very well. So a new method had to be devised. Take a look at main.m, in the Supporting Files group. You’ll see code like this:

@autoreleasepool {
	    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
	}

There now exists a brace-delineated block for autorelease pools. All objects created within the block are autoreleased; the exact moment of autorelease is still determined by the ARC system. Therefore, in cases when you need to create an autorelease pool and release it independently from the main one (perhaps in a tight loop where you’re creating many autoreleased objects), just enclose the entire loop in the new block.

New Keywords

In our conversion process, retain was replaced by strong. This makes sense, as retain is no longer available. strong means the same thing—it states it is a strong relationship, that the class owns (and should “retain”) the property. Its counterpart is weak, which means that it is a weak reference. This is especially important when you have a loop reference—for example, if you have a chess game, then you might have a Grid class which keeps track of many Pieces , but at the same time each Piece has a reference back to its owning Grid. In such a case, if the references in both directions were strong, then neither object could be released because they’d both have a +1 retain count to each other, and you’ll leak the memory. A weak reference prevents this issue because it does not actually increment the retain count.

Note though that weak references are only supported on iOS 5 and OS X Lion. In previous versions, you’ll have to use unsafe_unretained, which is a more general keyword that offers you less “protection” by the compiler. It also maintains a weak reference, but it doesn’t guarantee anything else; therefore, you have to make sure that the value is valid when you access it to prevent bad access errors.

If you want to declare ivars with the same characteristics (and you should, just to be clear and to help the compiler out), prefix the keywords with two underscores:

__strong, __weak, __unsafe_unretained

Minor Issues

In my experiences I’ve come across two anomalies when using ARC:

  • If you are using a switch statement and you are declaring a new local variable as the first line of each case, you’ll have to surround each case with curly braces immediately after the colon and after the break; or you’ll get a warning about the variable being out of scope.
  • There may be presentation issues with UIPopoverController on the iPad, where the application may crash with the error that the popover had been released before the popover had been dismissed. I will continue to investigate this issue and file a bug report with Apple if necessary. For the moment, a solution seems to be to create a strong property for it to always keep a reference to it.

ARC greatly simplifies app development on Apple platforms and allows it to catch up to other languages with garbage collection, without the overhead imparted by garbage collection (which is in fact significant on a mobile device like the iPhone; even desktop OS X apps rarely use GC). There are more obscure aspects of ARC than I’ve covered here, including bridged casting which you might see if you start working with C code or some lower-level Foundation and Core Foundation classes. I’ll talk about them as we come to them. If you’re curious, feel free to check out the official ARC documentation.

And as usual, download the newest version of the project here.

Objective-C Lesson 2: Basic Variables


In almost all programs, values may change; these values are stored in variables, which, as the name implies, are designed to have varying contents as the program is used. We’ll dive right in with a sample program.

Program 2.1

// Introducing variables

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	int i;
	i = 1;
	NSLog (@"The variable i has a value of %d", i);

	i = 2;
	NSLog (@"The variable i now has a value of %d", i);

    [pool drain];
    return 0;
}

The output of this simple program is as follows:

The variable i has a value of 1
The variable i has a value of 2

Code, Demystified

Program 2.1 contains the same “boilerplate” code in the template that Xcode provides for you. It is still not necessary for now, so explanation will be deferred.
The first “real” line of the program introduces the concept of a variable.

int i;

This variable is declared to be of type int and with the name i. Before we talk about the variable type, we have to take about the choice of the variable’s name.

Variable Names

In C, all variable names (in fact, almost all names in general) must be made of lowercase and uppercase letters, digits, and the underscore character (_). The first character of the name must be a letter or underscore. Because Objective-C is a strict superset of C, the same rules apply.

Note that C and Objective-C are case-sensitive. A lowercase letter is not the same as a capital letter. Therefore, fraction, Fraction, FRACTION, and fractioN are considered four completely different names. As a result, programmers often use camel case to declare variable names of multiple words. Examples include aPairOfDice, lotsOfMoney, and someAddress.

Because of the wide variety of options available to you, make sure to choose descriptive variable names, such as taxRate instead of t. You can only thank yourself later on.

Reserved Names

The Objective-C language reservescertain words, so that you cannot use them as a name; in fact, some are reserved by C itself. For a complete list, check out this post.

Data Types

In program 2.1, the variable i was declared to be of type int. This means that i can only hold a whole-number value, without a decimal point—an integer. Integer values are one of four basic data types. The others are floating-point values, characters, and boolean values. Floating-point values are simply numbers that include a fractional portion, represented as a decimal, such as 5.2 and 10.0. Characters are any single character that you can type on your keyboard, such as ‘A’ and ‘@’ and few others. Boolean values are either true or false; in Objective-C they represented as YES and NO.

Beyond the basics, C also defines three “adjectives” to modify the size of variables. These are short, long, and unsigned. An integer declared to be short will take up less memory than a standard int, and a long will take up more space. You can also use the specifier long long to indicate even larger values, but in OS X and iOS the additional long makes no difference. unsigned is used for values that will never be negative; this effectively doubles the maximum (positive) value of a variable without doubling its storage requirement (because every single negative value can now be “mapped” to a larger positive value).

short and long cannot be used in conjunction with each other in the same variable, but unsigned, or its optional counterpart signed, may be used on any variable, short, regular, or long.

Initialization

After declaring a variable, you have to initialize it with a value; otherwise, the variable would be useless. You can do this in a single statement, along with the declaration:

int variable = 1;

Alternatively, you can declare the variable, and then initialize it sometime later (it does not have to be the next statement):

int variable;
// Some code
variable = 1;

Printing Variables

Program 2.1 introduces a few new concepts in the NSLog statements.

NSLog (@"The variable i has a value of %d", i);

The real magic happens with the %d. This is a format specifier, and tells NSLog that it should expect a variable of a certain data type, and to print the value of that variable. In this case, the type %d tells NSLog to expect an integer value, and it does receive i as an argument, which is an integer value. Therefore, NSLog looks for the value of i, and prints the integer value. This explains the output:

The variable i has a value of 1

In the next few lines, we set i to the value 2, and then use NSLog to print it.

Note that the specifier %i is also used to print integer values.

Program 2.2

// Arithmetic operations with variables
#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    int result;
	result = 10 + 20;
	NSLog(@"The sum of 10 and 20 is %i", result);

	result = 25 - 30;
	NSLog(@"The difference between 25 and 30 is %i", result);

	result = 8 * 9;
	NSLog(@"The product of 8 and 9 is %i", result);

	NSLog(@"The quotient of 8 and 5 is %i", (8 / 5));
	NSLog(@"The quotient of 8.0 and 5.0 is %.1f", (8.0 / 5.0));	[pool drain];
    return 0;
}

The output is:

The sum of 10 and 20 is 30
The difference between 25 and 30 is -5
The product of 8 and 9 is 72
The quotient of 8 and 5 is 1
The quotient of 8.0 and 5.0 is 1.6

Program 2.2 presents some interesting situations. To begin, we define an integer value, and name it result. We set result equal to 10 + 20. Then we have the NSLog statement print out the value of the variable result, and we use the integer specifier because the result is an integer. The next few lines does the same, but note that negative values are supported. Multiplication works as expected, but the last two NSLogs deserve some attention.

The first thing to note is that you can specify an expression as an argument to NSLog, not simply a variable. As long as the result is of the proper type, NSLog doesn’t care whether you specify an expression or a variable.

Next, note that the first division results in a value of 1. Obviously, 8 ÷ 5 ≠ 1, but this is not a mathematical error. Remember that the format specifier %i is responsible for printing integers. Also note that the values 8 and 5 are both integers. This is an example of integer division. Through integer division, only the whole-number portion of the result is reported—any fractional portions are discarded.

To get the accurate result, we have to use floating-point numbers. These numbers are only syntactically different from ints in that they include a .0 portion at the end—as in 8.0 and 5.0. To print a floating-point value, we use the format specifier %f. The .1 portion of the specifier tells NSLog to only print the first decimal point, and disregard any others that may follow. Here, we can see that the result is indeed 1.6.

  • Welcome

    My goal is to make CupsOfCocoa into a beautiful source for beginners to the iPhone platform to get started. Subscribe below for more, and stay tuned!

  • Contact Me

    If you need to contact me for any reason, feel free to send me an email.
  • The Giving Spirit

    If you've found this site helpful, would you consider donating a little sum? Any amount is appreciated...Thanks so much!

  • Roadmap

  • Enter your email address to follow this blog and receive notifications of new posts by email.

    Join 220 other followers

  • Back to the Past

    May 2020
    S M T W T F S
     12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31  
  • Time Machine

  • You count!

    • 622,650 views
  • Worldwide Stats

    free counters
%d bloggers like this: