淘先锋技术网

首页 1 2 3 4 5 6 7

http://blog.csdn.net/chuanyituoku/article/details/17371807

虽然这里讲的大部分知识以前都看过,但是时不时出现某些点让我如茅塞顿开; 以前经常会忘记一些细节,这篇文章可以更好的理解细节,巩固知识体系。

Ownership qualifiers

In Objective-C, ‘id’ or each object type is used for object variable types.

Object types are pointer types of Objective-C classes, such as NSObject *. ‘id’ type is used to hide its class name. ‘id’ is equivalent to void* in the C language.

With ARC, ‘id’ and object type variables must have one of the following four ownership qualifiers:

  • __strong
  • __weak
  • __unsafe_unretained
  • __autoreleasing

You should decide which ownership qualifier is to be used for all the ‘id’ and object type variables in your source code. In this chapter, I explain how you should choose each qualifier one by one.

__strong ownership qualifier

The __strong ownership qualifier is used as the default for ‘id’ and object types. It means that the variable obj in the following source code is __strong qualified implicitly.

id obj = [[NSObject alloc] init];

Without being explicitly qualified, ‘id’ or objects are treated as __strong. The above code is the same as

id __strong obj = [[NSObject alloc] init];

The following is the same source code for a non-ARC environment.

id obj = [[NSObject alloc] init];

There is no difference thus far. Let’s see the next example.

{     id __strong obj = [[NSObject alloc] init]; }

Local variable scope is added intentionally. The non-ARC version for this source code is:

This means the “release” method is automatically added on an ARC environment to release the created object with ownership. When the control flow leaves the scope of the variable obj, the release method is called for the variable because the variable obj is qualified with __strong.

As it is named __strong, this ownership qualifier indicates a strong reference for the object. When the control flow leaves the variable scope, the strong reference disappears and the assigned object is released. Let’s find ownership status in the source code.

{     id __strong obj = [[NSObject alloc] init]; }

This code creates an object and has ownership of it. We add comments on the ownership status.

{      
    id __strong obj = [[NSObject alloc] init];
     
}
  
Assigning to __strong ownership qualified variables

The ownership and lifetime of the object are very clear. Next, we see what happens when you obtain an object without creating it yourself or having ownership.

{     id __strong obj = [NSMutableArray array]; }

It calls the class method “array” of NSMutableArray to obtain an object without creating it yourself or having ownership.

Ownership and lifetime of the object are very clear in this case as well. And, of course, you can exchange values between variables qualified with __strong as follows.

id __strong obj0 = [[NSObject alloc] init]; id __strong obj1 = [[NSObject alloc] init]; id __strong obj2 = nil; obj0 = obj1; obj2 = obj0; obj1 = nil;
obj0 = nil; obj2 = nil;
How the strong reference works

We investigate the example with comments to understand how the strong reference works as in Listing 2–1.

Listing 2–1. How the strong reference works

id __strong obj0 = [[NSObject alloc] init];  
 
id __strong obj1 = [[NSObject alloc] init];  
 
id __strong obj2 = nil;
 
obj0 = obj1;
 
obj2 = obj0;
 
obj1 = nil;
 
obj0 = nil;
 
obj2 = nil;
 

As in the example (Listing 2–1), ownership is properly managed not only by variable scope, but also by assignments between variables, which are qualified with __strong. Of course, a __strong qualifier can be used as a member variable of Objective-C class or any argument of methods as in Listing 2–2.

Listing 2–2. _Strong qualifiers for member variable and method’s argument

@interface Test : NSObject {     id __strong obj_; } - (void)setObject:(id __strong)obj; @end
@implementation Test - (id)init {     self = [super init];     return self; }
- (void)setObject:(id __strong)obj
{     obj_ = obj; } @end

Let’s see how we can use the class.

{     id __strong test = [[Test alloc] init];     [test setObject:[[NSObject alloc] init]]; }

As usual, let’s investigate line by line with some comments (Listing 2–3).

Listing 2–3. _Strong qualifiers for member variable and method’s argument with comments

{     id __strong test = [[Test alloc] init];

[test setObject:[[NSObject alloc] init]];
 
}  

As the example above shows, it is very easy to use a __strong ownership qualifier for a class member and an argument of a method. In a later section, I explain how it can be used for a class property.

By the way, any variables that are qualified with __strong, __weak, and __autoreleasing, are initialized with nil. Let’s see it with an example.

id __strong obj0; id __weak obj1; id __autoreleasing obj2;

The above source code is equivalent to the following.

id __strong obj0 = nil; id __weak obj1 = nil; id __autoreleasing obj2 = nil;

As we saw earlier, Apple says that you don’t need to type retain or release any more. Please note that the following rules for reference counting are still fulfilled.

  • You have ownership of any objects you create.
  • You can take ownership of an object using retain.
  • When you no longer need it, you must relinquish ownership of an object of which you have ownership.
  • You must not relinquish ownership of an object of which you don’t have ownership.

The first two rules are achieved by assigning to a __strong variable. The third rule is achieved automatically by leaving variable scope, by assigning to variables, or when objects having a member variable are discarded. The last rule is very clear because releases are never typed. So all the rules are still fulfilled.

You don’t even need to type __strong because it is the default for ‘id’ and object types. By just enabling ARC, the rules are fulfilled automatically.

__weak ownership qualifier

It may appear that the compiler can perform memory management with only the __strong ownership qualifier. Unfortunately, that’s not the case because one big problem can’t be solved: circular reference [Figure 2–2]. The following describes how circular reference occurs.

images

Figure 2–2. Circular reference

Circular Reference

Examples in this section show how a circular reference occurs. For instance, it occurs when a member variable of a class has a reference to an object. Assume that we have a class as in Listing 2–4.

Listing 2–4. _A class that may cause circular reference

@interface Test : NSObject {     id __strong obj_; } - (void)setObject:(id __strong)obj; @end
@implementation Test - (id)init {     self = [super init];     return self; }
- (void)setObject:(id __strong)obj {     obj_ = obj; } @end

We can easily produce a circular reference problem with the class as in Listing 2–5.

Listing 2–5. Produces circular reference

{     id test0 = [[Test alloc] init];
    id test1 = [[Test alloc] init];     [test0 setObject:test1];     [test1 setObject:test0]; }

Adding a few comments opens this example up a little bit (Listing 2–6).

Listing 2–6. Produces circular reference with comments

{     id test0 = [[Test alloc] init];
     
    id test1 = [[Test alloc] init];
    
    [test0 setObject:test1];
     
    [test1 setObject:test0];
     
}  

Figure 2–3 illustrates this situation.

images

Figure 2–3. Circular reference with class member variables

With circular reference, memory leaks happen very often. A memory leak means that some objects still remain in memory even after they are thought to be discarded.

In the example, when the control flow leaves the variable scope of test0 and test1, object A and object B were planned to be discarded. At this point, they should be disposed of because no one has an entry point to the objects. But because of circular reference, they remain.

Self Reference

The next example shows that even one object can produce a circular reference by referencing the object itself. It is sometimes called “self reference” as shown in Figure 2–4.

id test = [[Test alloc] init]; [test setObject:test];
images

Figure 2–4. Self reference

How can we avoid this situation? As we have a __strong ownership qualifier, you might have noticed there is a __weak ownership qualifier. By using a __weak ownership qualifier, we can avoid a circular reference as shown in Figure 2–5.

A __weak ownership qualifier provides a weak reference. A weak reference does not have ownership of the object. Let’s see the next example.

id __weak obj = [[NSObject alloc] init];

The variable obj is qualified with __weak. When the source code is compiled, the compiler shows a warning message.

warning: assigning retained obj to weak variable; obj will be           released after assignment [-Warc-unsafe-retained-assign]             id __weak obj = [[NSObject alloc] init];                       ^     ~~~~~~~~~~~~~~~~~~~~~~~

In the example, a created object is assigned to a variable obj, which has been qualified with __weak. So, the variable obj has a weak reference to the object, which means the variable doesn’t have ownership. Because no one can take ownership of the created object, the object will be released just after creation. The compiler warns about that case. If you assign the object to a __strong variable at first, the warning will disappear as follows.

{     id __strong obj0 = [[NSObject alloc] init];     id __weak obj1 = obj0; }

Let’s see the source code with comments on ownership status.

{      
    id __strong obj0 = [[NSObject alloc] init];
     
    id __weak obj1 = obj0;
     
}  

Because a __weak reference does not have ownership, the object is disposed of when the control flow leaves the variable scope. Now we’ve learned that we should use the __weak ownership qualifier to avoid cyclic reference. We can rewrite the previous example as follows.

@interface Test : NSObject {     id __weak obj_; } - (void)setObject:(id __strong)obj; @end
images

Figure 2–5. Avoid circular reference with the __weak ownership qualifier

Weak Reference Disappears

And, there is one more thing you should know about the __weak ownership qualifier. When a variable has a reference to an object and the object is discarded, the weak reference also disappears automatically, which means that the variable is assigned to nil. Let’s check it with an example (Listing 2–7).

Listing 2–7. Weak reference disappears

id __weak obj1 = nil; {     id __strong obj0 = [[NSObject alloc] init];
    obj1 = obj0;
    NSLog(@"A: %@", obj1); }
NSLog(@"B: %@", obj1);

The result is:

A: <NSObject: 0x753e180> B: (null)

Let’s see the source code again with comments on ownership status (Listing 2–8).

Listing 2–8. Weak reference disappears with comments

id __weak obj1 = nil;
{      
    id __strong obj0 = [[NSObject alloc] init];
     
    obj1 = obj0;
     
    NSLog(@"A: %@", obj0);
     
}  
NSLog(@"B: %@", obj1);
 

With the __weak ownership qualifier, we can avoid cyclic reference. Also by checking if the __weak variable is nil, we can know if the object still exists or is already discarded.

Unfortunately the __weak ownership qualifier is usable only for applications targeting iOS5 (or later) or OSX Lion (or later). For applications targeting older environments, __unsafe_unretained must be used instead.

__unsafe_unretained ownership qualifier

The __unsafe_unretained ownership qualifier is, as the name suggests, absolutely unsafe. Normally, due to ARC, the compiler executes memory management. But any variables qualified with __unsafe_unretained are excluded from the mechanism. So, you have to take care of the variables manually.

id __unsafe_unretained obj = [[NSObject alloc] init];

This source code is to assign an object to a variable, which is qualified with __unsafe_unretained. The compiler shows the following warning message.

__unsafe_unretained variables don’t have ownership of an object, the same as __weak. In the above example, the object is released just after the object is created. It seems that __unsafe_unretained and __weak qualifier work the same. You will see how __unsafe_unretained is different from __weak in the following (Listing 2–9).

Listing 2–9. _unsafe_unretained qualifier

id __unsafe_unretained obj1 = nil;
{     id __strong obj0 = [[NSObject alloc] init];
    obj1 = obj0;
    NSLog(@"A: %@", obj1); }
NSLog(@"B: %@", obj1);

The result is:

A: <NSObject: 0x753e180> B:

Let’s see the source code again with comments as usual (Listing 2–10).

Listing 2–10. _unsafe_unretained qualifier with comments

id __unsafe_unretained obj1 = nil;
{      
    id __strong obj0 = [[NSObject alloc] init];
    
    obj1 = obj0;
     
    NSLog(@"A: %@", obj1);
     
}  
NSLog(@"B: %@", obj1);
 

Though the last NSLog looks fine, it just works by chance. The application crashes depending on conditions. To assign an object to a variable qualified with __unsafe_unretained, you have to make sure that the object exists and has been assigned to some variables qualified with __strong. I recommend reconsidering the reason why you need __unsafe_unretained. You need to understand why completely; for example, you might need it because the application has to support iOS4 or OS X Snow Leopard and you need to use it instead of __weak. Also, you have to be sure that the assigned object exists during the lifetime of the variable. If it fails, the application will crash and no one will use your application.

__autoreleasing ownership qualifier

Let’s see if there are any changes related to autorelease with ARC. To put simply, you can’t use the autorelease method. You can’t use the NSAutoreleasePool class either. I explain it later with some rules. Although you can’t directly use autorelease, there is a mechanism of autorelease still. Without ARC, we’ve used autorelease as follows.


NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];

This source code can be rewritten for an ARC-enabled environment.

@autoreleasepool {
    id __autoreleasing obj = [[NSObject alloc] init];
}

Instead of creating, having, and discarding an NSAutoreleasePool class object, you have to enclose codes with @autoreleasepool block. Instead of calling the autorelease method, you have to assign the object to a variable qualified with __autoreleasing. Assigning the object to the __autoreleasing qualified variable is equivalent to calling autorelease on a non-ARC environment. By doing it, the object is registered to an autoreleasepool. So, with ARC enabled, you just have to use @autoreleasepool instead of NSAutoreleasePool class and use the __autoreleasing qualified variable instead of the autorelease method as shown in Figure 2–6.

images

Figure 2–6. @autoreleasepool and a variable with __autoreleasing qualifier

But in reality, it is very rare to type __autoreleasing explicitly. Let’s see why you don’t need to type __autoreleasing very often.

Compiler Cares __autoreleasing Automatically

To obtain an object without creation, you use some methods not in the alloc/new/copy/mutableCopy method group. In this case, the object is automatically registered to the autoreleasepool. It is same as obtaining an autoreleased object.When an object is returned from a method, the compiler checks if the method begins with alloc/new/copy/mutableCopy, and if not, the returned object is automatically registered to the autorelease pool. Exceptionally, any method whose name begins with init, doesn’t register the return value to autoreleasepool. Please see below for more about this new rule: the naming rule for methods related to object creation must be followed.

@autoreleasepool {     id __strong obj = [NSMutableArray array]; }

Let’s see the source code with comments on ownership status.

@autoreleasepool {     
    id __strong obj = [NSMutableArray array];
     
}     

As the example shows, objects are registered to an autoreleasepool even without the __autoreleasing qualifier. Next, let’s see a sample implementation of the method, which is used in the above example to obtain an object without creating it.

+ (id) array {     return [[NSMutableArray alloc] init]; }

This code also doesn’t use the __autoreleasing qualifier. It too can be written as follows.

+ (id) array {
    id obj = [[NSMutableArray alloc] init];
    return obj;
}

“id obj” does not have a qualifier. So it is qualified with __strong. When the “return” sentence is executed, the variable scope is left and the strong reference disappears. Therefore the object will be released automatically. Before that, if the compiler detects that the object will be passed to the caller, the object is registered in autoreleasepool.

The next example is about the __weak ownership qualifier. As you know, the __weak ownership qualifier is used to avoid cyclic reference. When a variable with a __weak qualifier is used, the object is always registered in autoreleasepool.

id __weak obj1 = obj0; NSLog(@"class=%@", [obj1 class]);

The above source code is equivalent to:

id __weak obj1 = obj0; id __autoreleasing tmp = obj1; NSLog(@"class=%@", [tmp class]);

Why does the object need to be registered in autoreleasepool in order to use the object via the __weak qualified variable? Because a variable, which is qualified with __weak, does not have a strong reference, the object might be disposed of at any point. If the object is registered in autoreleasepool, until @autoreleasepool block is left, the object must exist. So, to use the objects via __weak variable safely, the object is registered in autoreleasepool automatically.

Let’s see one more final example about implicit __autoreleasing. As explained, “id obj” is the same as “id __strong obj”.

How about “id *obj” ? Does it mean “id __strong *obj” ?

The answer is “id __autoreleasing *obj”.

And, “NSObject **obj” means “NSObject * __autoreleasing *obj”.

Any pointers to ‘id’ or object types are qualified with __autoreleasing as default.

Sometimes, a method passes the error information via an argument with a pointer type of NSError object instead of its return value. Cocoa Framework uses this technique for many methods such as the NSString class method stringWithContentsOfFile:encoding:error. It is like:

NSError *error = nil; BOOL result = [obj performOperationWithError:&error];

The declaration of this method is:

- (BOOL) performOperationWithError:(NSError **)error;

Because the pointer to ‘id’ or object types is qualified with __autoreleasing as default, this declaration is equivalent to:

- (BOOL) performOperationWithError:(NSError * __autoreleasing *)error;
Returning a Result as the Argument

Methods taking NSError pointer as an argument need to create an NSError object itself depending on the result. The caller will obtain the object as an argument, which means that the caller does not obtain it from the alloc/new/copy/mutableCopy method group. To follow the memory management rules, when you do not obtain an object by the alloc/new/copy/mutableCopy method group, the object has to be passed without ownership. By the __autoreleasing ownership qualifier, the rule is fulfilled.

For example, performOperationWithError is implemented as follows.

- (BOOL) performOperationWithError:(NSError * __autoreleasing *)error {     
    return NO; }

By assigning to *error, which is NSError * __autoreleasing * type, an object can be passed to its caller after being registered in autoreleasepool.

The following source code causes a compile error.

To assign an object pointer, both ownership qualifiers have to be the same. The compiler shows the following error.

error: initializing 'NSError *__autoreleasing *' with an expression           of type 'NSError *__strong *' changes retain/release properties of pointer         NSError **pError = &error;                               ^        ~~~~~~

In this case, a __strong ownership qualifier has to be added.

NSError *error = nil; NSError * __strong *pError = &error;

This rule applies to all the other ownership qualifiers as well.

NSError __weak *error = nil; NSError * __weak *pError = &error;
NSError __unsafe_unretained *unsafeError = nil; NSError * __unsafe_unretained *pUnsafeError = &unsafeError;

By the way, in the previous example, the method takes an argument as the pointer of object type, which is qualified with __autoreleasing.

- (BOOL) performOperationWithError:(NSError * __autoreleasing *)error;

And the caller passes the pointer of object type, which qualified with __strong.

NSError __strong *error = nil; BOOL result = [obj performOperationWithError:&error];

As we have seen, to exchange object pointers, both variables must be identically qualified. But the example is not. How does it work? The following explains how the compiler handles it.

NSError __strong *error = nil; NSError __autoreleasing *tmp = error; BOOL result = [obj performOperationWithError:&tmp]; error = tmp;

Also, you can type the ownership qualifier explicitly for the object pointer type as follows.

- (BOOL) performOperationWithError:(NSError * __strong *)error;

This declaration shows how to pass an object without registering it to autoreleasepool. Although it is possible, you should not do that. From the caller’s point of view, to create an object with ownership, the method has to be in the alloc/new/copy/mutableCopy group. If it is not in the group, the caller should obtain an object without ownership. So, the argument should be qualified with __autoreleasing.

And, when you use the __autoreleasing ownership qualifier explicitly, the variable has to be an automatic variable, such as a local variable, or an argument of method/function.

Next, let’s see @autoreleasepool a bit more. In a non-ARC environment, autoreleasepools are used as follows.


NSAutoreleasePool *pool0 = [[NSAutoreleasePool alloc] init];     NSAutoreleasePool *pool1 = [[NSAutoreleasePool alloc] init];         NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];         id obj = [[NSObject alloc] init];         [obj autorelease];         [pool2 drain];     [pool1 drain]; [pool0 drain];

The @autoreleasepool block can be nested as well.

@autoreleasepool {     @autoreleasepool {         @autoreleasepool {             id __autoreleasing obj = [[NSObject alloc] init];         }     } }

For instance, Application’s main function, which is generated from the iOS application template, has @autoreleasepool block to enclose all the application code.

int main(int argc, char *argv[]) {     @autoreleasepool {         return UIApplicationMain(argc, argv, nil,             NSStringFromClass([AppDelegate class]));     } }

Also, NSRunLoop has autoreleasepool to release all the objects once in each loop. This mechanism is the same both on ARC-enabled and -disabled environments. By the way, @autoreleasepool block can be used even in a non-ARC environment, but it has to be compiled with an LLVM compiler 3.0 or later. In this case, the source code should be as follows.

@autoreleasepool {     id obj = [[NSObject alloc] init];     [obj autorelease]; }

I recommend using @autoreleasepool even in a non-ARC environment, rather than NSAutoreleasePool. The reason is that the scope of autoreleasepool is written as blocks, so its readability is far better.

Also, the _objc_autoreleasePoolPrint() (see the implementation by Apple in Chapter 1) can be used for both an ARC-enabled or -disabled environment.

_objc_autoreleasePoolPrint();

Please use it effectively to debug objects in autoreleasepool.

__strong and __weak

The concept of __strong and __weak variables is very similar to that of smart pointers in C++. They are called std::shared_ptr and std::weak_ptr. std::shared_ptr uses reference counting to manage ownership of C++ class objects. Std::weak_ptr is used to avoid cyclic reference. If you have to use C++ some time, using these smart pointers is strongly recommended.

* -1999 boost::shared_ptr is part of the Boost C++ library

* 2002 boost::weak_ptr is added to the library

* 2005 tr1::shared_ptr and tr1::weak_ptr are adopted by standard C++ Library draft TR1. In some environments, std::shared_ptr and std::weak_ptr can be used

* std::shared_ptr and std::weak_ptr are adopted by C++Standard C++11 (known as C++0x)

转载于:https://www.cnblogs.com/xuejinhui/p/4236304.html