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
.
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 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
-
(
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 __auto
releasing
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.