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.
Study the new storage attributes introduced with the latest
LLVM compiler: strong
, weak
, and unsafe_unretained
.
In the latest LLVM complier, to use Automatic Reference Counting (ARC), we will 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 tonil
.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 @synthesize window = _window; @synthesize string1; @synthesize string2; - (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.
Note
If you look closely at the implementation file, you will
notice that the window
property
of our app delegate is synthesized on the _window
instance variable of the class.
This is how Xcode by default sets up our app delegate and we won’t
be touching it in any of the examples in this book.
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 emphasis 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:
/* 3 */ - (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 5 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.