1. Trang chủ >
  2. Công Nghệ Thông Tin >
  3. Kỹ thuật lập trình >

16  Moving from Manual Reference Counting to Automatic Reference Counting

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



Xem Thêm
Tải bản đầy đủ (.pdf) (900 trang)

×