Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (24.65 MB, 900 trang )
Discussion
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 run-time 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 objct 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
@interface Moving_from_Manual_Reference_Counting_to_ARCAppDelegate
: UIResponder
@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 value 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
1.16 Moving from Manual Reference Counting to Automatic Reference Counting | 55
www.it-ebooks.info
@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;
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 is __weak, and unsafe_unre
tained 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 zeroing weak referencing under ARC is really magical and solves many issues programmers used to face when their objects were being deallocated. The magic in weak
56 | Chapter 1: The Basics
www.it-ebooks.info
referencing is that a zeroing weak reference's object gets set to nil when the object that
it points to gets deallocated. The deallocation of that object will go through all the weak
referencing pointers that point to that object and one by one, will set them to nil.
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
@interface Moving_from_Manual_Reference_Counting_to_ARCAppDelegate
: UIResponder
@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 runloop 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 bringing
another app to the foreground.) Once we're running in the background, we know that
the runloop 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];
1.16 Moving from Manual Reference Counting to Automatic Reference Counting | 57
www.it-ebooks.info
}
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
@interface Moving_from_Manual_Reference_Counting_to_ARCAppDelegate
: UIResponder
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic, strong) NSString *string1;
@property (nonatomic, unsafe_unretained) NSString *string2;
@end
Now if 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 get a 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 __autor
eleasing specifier. This storage specifier is most handy when we want to pass an object
by reference to a method. 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 really allocate this error, so our method should do so. To do this, you must specify that this
error prameter 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];
58 | Chapter 1: The Basics
www.it-ebooks.info
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.
See Also
XXX
1.17 Typecasting with Automatic Reference Counting
Problem
You want to know how to use new typecasting facilities under Automatic Reference
Counting.
Solution
Use the __bridge, __bridge_transfer, and __bridge_retained typecasting specifiers.
1.17 Typecasting with Automatic Reference Counting | 59
www.it-ebooks.info
Discussion
Typecasting is the process of pointing one value of type A to another value of type B.
For instance, if you have a Core Foundation string object of type CFStringRef and you
would like to place it inside an Objective-C string of type NSString, you can easily create
an error:
- (BOOL)
application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
CFStringRef coreFoundationString =
CFStringCreateWithCString(CFAllocatorGetDefault(),
"C String",
kCFStringEncodingUTF8);
/* Compile time error!!! */
NSString *objCString = coreFoundationString;
self.window = [[UIWindow alloc] initWithFrame:
[[UIScreen mainScreen] bounds]];
}
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
Here we are assigning the value of the Core Foundation string coreFoundationString
to the Objective-C string of type NSString named objCString, our compiler will get
confused because it doesn't know what we are intending to do with the memory assigned to each one of these objects. Additionally, we will end up with a memory leak
because the compiler doesn't know how to get rid of the Core Foundation object for
us automatically. Remember that Automatic Reference Counting does not work for
Core Foundation objects, so we need to assist the compiler. To do this, let's try to
understand what each one of these typecasting specifiers does:
__bridge
Simply typecasts the object on the right side of the equation to the left side. This
will not modify the retain count on any of the objects; neither the one on the left
nor the one on the right side of the equation.
__bridge_transfer
This typecast will assign the object on the right side to the object on the left and
will release the object on the right side. So if you have a Core Foundation string,
like the one we saw before, that you have just created and want to place it inside
a local variable of type NSString (local variables are by default strong, see Recipe 1.16), then you should use this typecasting option because then you wouldn't
have to release the Core Foundation string after the assignment. We will see an
exmaple of this soon.
60 | Chapter 1: The Basics
www.it-ebooks.info