1.17. Moving from Manual Reference Counting to Automatic Reference Counting

Problem

You want to learn about Automatic Reference Counting, Apple’s new Compiler solution to solving the headache that programmers had to deal with when working with objects and memory management in Objective-C.

Note

Automatic Reference Counting eliminates many of the manual reference counting issues that ultimately resulted in iOS apps that would crash here and there and would be very unstable when deployed on user devices. ARC removes this headache by leaving most of the memory management complexity to the compiler.

Solution

Study the new storage attributes introduced with the latest LLVM compiler: strong, weak, and unsafe_unretained.

Discussion

To use Automatic Reference Counting (ARC) in the latest LLVM compiler, we need to deal with storage that is strong, weak, or unsafe and unretained. Any object under ARC is managed with one of these storage attributes. Here is a short explanation for each one:

strong

An object of this type is automatically retained at runtime and will be valid until the end of its scope, where it will automatically be released. For those familiar with Objective-C’s traditional way of memory management, this keyword is similar to the retain keyword.

weak

This is zeroing weak referencing. If a variable is defined with this keyword, when the object to which this variable points gets deallocated, this value will get set to nil. For instance, if you have a strong string property and a weak string property and set the weak property’s value to the strong property’s value, when the strong property gets deallocated, the weak property’s value will get set to nil.

unsafe_unretained

This is simply pointing one variable to another. This will not retain the object into the new variable, it will simply assign the object to the variable.

By default, all local variables are strong variables. In contrast, properties must explicitly specify their storage attribute. In other words, the compiler won’t assume that all properties without a storage attribute are by default strong properties. So do make sure that you specify the storage attributes for your properties. Let’s have a look at an example of the strong storage attribute. Let’s assume we have two properties called string1 and string2:

#import <UIKit/UIKit.h>

@interface Moving_from_Manual_Reference_Counting_to_ARCAppDelegate
           : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (nonatomic, strong) NSString *string1;
@property (nonatomic, strong) NSString *string2;

@end

Now if we initialize the string1 property with the string value of String 1 and assign this property’s value to the string2 property, we will see that with the strong storage attribute, the string2 property will keep its value even after string1 is deallocated:

#import "Moving_from_Manual_Reference_Counting_to_ARCAppDelegate.h"

@implementation Moving_from_Manual_Reference_Counting_to_ARCAppDelegate

- (BOOL)            application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{  
  self.string1 = @"String 1";
  self.string2 = self.string1;
  self.string1 = nil;

  NSLog(@"String 2 = %@", self.string2);

  self.window = [[UIWindow alloc] initWithFrame:
                 [[UIScreen mainScreen] bounds]];

  self.window.backgroundColor = [UIColor whiteColor];
  [self.window makeKeyAndVisible];
  return YES;
}

Note

Memory allocated for an object is disposed of when all strong variables pointing to that memory are deallocated.

The output of this program is this:

String 2 = String 1

the strong, weak, and unsafe_unretained are most frequently used when declaring properties. You can take advantage of these storage specifiers even when declaring local variables, but you need to change the specifiers a bit. The strong specifier’s inline equivalent is __strong, weak specifier’s inline equivalent is __weak, and unsafe_unretained specifier’s inline equivalent is __unsafe_unretained. (Note that each of those keywords begins with two underline characters.) Here is an example:

- (BOOL)            application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

  /* All local variables are by default strong, so just emphasize that. We
   really don't have to mention __strong for the first variable but
   to make it clear, we will set it. No harm in doing so. */
  __strong NSString *yourString = @"Your String";
  __weak   NSString *myString = yourString;
  yourString = nil;
  __unsafe_unretained NSString *theirString = myString;

  /* All pointers will be nil at this time */
  self.window = [[UIWindow alloc] initWithFrame:
                 [[UIScreen mainScreen] bounds]];

  self.window.backgroundColor = [UIColor whiteColor];
  [self.window makeKeyAndVisible];
  return YES;
}

the unsafe_unretained storage specifier is truly unsafe, as its name implies. The reason for it being unsafe is if the object to which an unsafe_unretained variable points gets deallocated, this variable will not get set to nil and will point to a dangling location in the memory. Accessing this location might cause your application to crash. To avoid this, you should be using the zeroing weak referencing storage specifier, weak or its inline equivalent __weak.

Let’s see an example for zeroing weak referencing. Let’s change our string2 property’s storage specifier to weak instead of strong:

#import <UIKit/UIKit.h>

@interface Moving_from_Manual_Reference_Counting_to_ARCAppDelegate
           : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (nonatomic, strong) NSString *string1;
@property (nonatomic, weak) NSString *string2;

@end

When our app starts for the first time, we will initialize the strong string1 property and will assign string1 to string2. We will then set the value of the string1 property to nil. Then we will wait. This is absolutely crucial. If immediately after setting the value of string1 to nil, you print out the value of string2, chances are that you will get incorrect results instead of nil. So you need to make sure that your app’s run loop has gotten rid of all invalidated objects. In order to achieve this, we will print the value of strong2 when our app gets sent to the background. (This is caused by the user pressing the home button on their iOS device.) Once we’re running in the background, we know that the run loop has already gotten rid of invalidated objects in the memory and the results that we will get will be accurate:

- (BOOL)            application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

  self.string1 = [[NSString alloc] initWithUTF8String:"String 1"];
  self.string2 = self.string1;
  self.string1 = nil;

  /* All pointers will be nil at this time */

  self.window = [[UIWindow alloc] initWithFrame:
                 [[UIScreen mainScreen] bounds]];

  self.window.backgroundColor = [UIColor whiteColor];
  [self.window makeKeyAndVisible];
  return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application{
  NSLog(@"String 2 = %@", self.string2);
}

Now run this app, wait a second or two, and press the Home button on the device/simulator. You will notice that the following results will get printed to the console window:

String 2 = (null)

This easily proved that the zeroing weak references work perfectly under ARC. Now to check how dangerous the unsafe_unretained storage specifier is, let’s go ahead and change the string2 property’s storage specifier to unsafe_unretained and repeat the exact same practice as we did for the weak property:

#import <UIKit/UIKit.h>

@interface Moving_from_Manual_Reference_Counting_to_ARCAppDelegate
           : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (nonatomic, strong) NSString *string1;
@property (nonatomic, unsafe_unretained) NSString *string2;

@end

Now if you leave the implementation of your app delegate as we had implemented it in the previous example (printing the value of string2 property when our app gets sent to the background), and you repeat the same procedure and open your app and send it to the background, you will crash! This means that when our app was sent to the background, we tried to print out the contents of an invalidated memory location that the string2 property was pointing to. Since the string2 property was unsafe and unretained, it didn’t know that the object that it was pointing to (in string1) was already deallocated when string1 was set to nil.

In addition to the aforementioned three storage specifiers, we can also use the __autoreleasing specifier. This storage specifier is most handy when we want to pass an object by reference to a method. This is required for when you want to call a method and leave that method responsible for allocating, initializing, and returning an instance of a class. The caller method will then have no responsibility at all to release the returned instance and will leave it to the runtime to decide when it is best to release the allocated instance of that class (handled by autorelease pools). For instance, if you have a method that needs to pass an error of type NSError to the caller method, the caller method will pass an uninitialized and unallocated instance of NSError to this method. This means the caller didn’t allocate memory for this error variable, so our method should do so. To do this, you must specify that this error parameter needs to be automatically released by the runtime when the right time comes:

- (void) generateErrorInVariable:(__autoreleasing NSError **)paramError{

  NSArray *objects = [[NSArray alloc] initWithObjects:@"A simple error", nil];

  NSArray *keys =
  [[NSArray alloc] initWithObjects:NSLocalizedDescriptionKey, nil];

  NSDictionary *errorDictionary = [[NSDictionary alloc] initWithObjects:objects
                                                                forKeys:keys];

  *paramError = [[NSError alloc] initWithDomain:@"MyApp"
                                           code:1
                                       userInfo:errorDictionary];
}

- (BOOL)            application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

  NSError *error = nil;
  [self generateErrorInVariable:&error];

  NSLog(@"Error = %@", error);

  self.window = [[UIWindow alloc] initWithFrame:
                 [[UIScreen mainScreen] bounds]];

  self.window.backgroundColor = [UIColor whiteColor];
  [self.window makeKeyAndVisible];
  return YES;
}

In this example, the application:didFinishLaunchingWithOptions: method didn’t allocate the instance of NSError; the generateErrorInVariable method did. But for the compiler to understand the scope of the error object, the generateErrorInVariable method mentioned to the compiler that the object which will be created into its error parameter needs to be automatically released if it is no longer needed.

Get iOS 6 Programming Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.