iPhone之IOS5記憶體管理(ARC技術概述)

weixin_34402090發表於2013-08-03

ARC(Automatic Reference Counting )技術概述

此文章由Tom翻譯,首發於csdn的blog,任何人都可以轉發,但是請保留原始連結和翻譯者得名字。多謝!

 

Automatic Reference Counting (ARC) 是一個編譯期的技術,利用此技術可以簡化Objective-C程式設計在記憶體管理方面的工作量。

這裡我把此技術翻譯為自動記憶體計數器管理技術,下圖是使用和不使用此技術的Objective-C程式碼的區別。


ARC技術是隨著XCode4.2一起釋出的,在預設工程模板中,你可以指定你的工程是否支援ARC技術,如果你不指定工程支援ARC技術,在程式碼中你必須使用管理記憶體的程式碼來管理記憶體。

概述

自動計數(ARC)是一個編譯期間工作的能夠幫你管理記憶體的技術,通過它,程式人員可以不需要在記憶體的retain,釋放等方面花費精力。

ARC在編譯期間為每個Objective-C指標變數新增合適的retain, release, autorelease等函式,儲存每個變數的生存週期控制在合理的範圍內,以期實現程式碼上的自動記憶體管理。

In order for the compiler to generate correct code, ARC imposes some restrictions on the methods you can use, and on how you use toll-free bridging (see “Toll-Free Bridged Types”); ARC also introduces new lifetime qualifiers for object references and declared properties.

你可以使用編譯標記-fobjc-arc來讓你的工程支援ARC。ARC在Xcode4.2中引入,在Mac OS X v10.6,v10.7 (64位應用),iOS 4,iOS 5中支援,Xcode4.1中不支援這個技術.

如果你現在的工程不支援ARC技術,你可以通過一個自動轉換工具來轉換你的工程(工具在Edit->Convert menu),這個工具會自動所有工程中手動管理記憶體的點轉換成合適自動方式的(比如移除retain, release等)。這個工具會轉換工程中所有的檔案。當然你可以轉換單個檔案。

ARC提供自動記憶體管理的功能

ARC使得你不需要再思考何時使用retain,release,autorelease這樣的函式來管理記憶體,它提供了自動評估記憶體生存期的功能,並且在編譯期間自動加入合適的管理記憶體的方法。編譯器也會自動生成dealloc函式。一般情況下,通過ARC技術,你可以不顧傳統方式的記憶體管理方式,但是深入瞭解傳統的記憶體管理是十分有必要的。

下面是一個person類的一個宣告和實現,它使用了ARC技術。

@interface Person : NSObject
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSNumber *yearOfBirth;
@property (nonatomic, strong) Person *spouse;
@end
 
@implementation Person
@synthesize firstName, lastName, yearOfBirth, spouse;
@end

(有關strong的幫助,請參考 “ARC Introduces New Lifetime Qualifiers.”)

使用ARC,你可以象下面的方式實現contrived函式:

- (void)contrived {
    Person *aPerson = [[Person alloc] init];
    [aPerson setFirstName:@"William"];
    [aPerson setLastName:@"Dudney"];
    [aPerson:setYearOfBirth:[[NSNumber alloc] initWithInteger:2011]];
    NSLog(@"aPerson: %@", aPerson);
}

ARC管理記憶體,所以這裡你不用擔心aPerson和NSNumber的臨時變數會造成記憶體洩漏。

你還可以象下面的方式來實現Person類中的takeLastNameFrom:方法,

- (void)takeLastNameFrom:(Person *)person {
    NSString *oldLastname = [self lastName];
    [self setLastName:[person lastName]];
    NSLog(@"Lastname changed from %@ to %@", oldLastname, [self lastName]);
}

ARC可以保證在NSLog呼叫的時候,oldLastname還存在於記憶體中。

ARC中的新規則

為了ARC能順利工作,特增加如下規則,這些規則可能是為了更健壯的記憶體管理,也有可能為了更好的使用體驗,也有可能是簡化程式碼的編寫,不論如何,請不要違反下面的規則,如果違反,將會得到一個編譯期錯誤。

  • 下面的這些函式:dealloc,retainreleaseretainCountautorelease。禁止任何形式呼叫和實現(dealloc可能會被實現),包括使用@selector(retain)@selector(release)等的隱含呼叫。

    你可能會實現一個和記憶體管理沒有關係的dealloc,譬如只是為了呼叫[systemClassInstance setDelegate:nil] ,但是請不要呼叫[super dealloc] ,因為編譯器會自動處理這些事情。

  • 你不可以使用NSAllocateObject或者NSDeallocateObject.

    使用alloc申請一塊記憶體後,其他的都可以交給執行期的自動管理了。

  • 不能在C語言中的結構中使用Objective-c中的類的指標。

    請使用類類管理資料。

  • 不能使用NSAutoreleasePool.

    作為替代,@autoreleasepool被引入,你可以使用這個效率更高的關鍵詞。

  • 不能使用memory zones.

    NSZone不再需要—本來這個類已經被現代Objective-c廢棄。

ARC在函式和便利變數命名上也有一些新的規定

  • 禁止以new開頭的屬性變數命名。

ARC Introduces New Lifetime Qualifiers

ARC introduces several new lifetime qualifiers for objects, and zeroing weak references. A weak reference does not extend the lifetime of the object it points to. A zeroing weak reference automatically becomes nil if the object it points to is deallocated.

You should take advantage of these qualifiers to manage the object graphs in your program. In particular, ARC does not guard against strong reference cycles (previously known as retain cycles—see “Practical Memory Management”). Judicious use of weak relationships will help to ensure you don’t create cycles.

屬性變數修飾符

weak和strong兩個修飾符是新引進的,使用例子如下:

// 下面的作用和: @property(retain) MyClass *myObject;相同
@property(strong) MyClass *myObject;
 
// 下面的作用和"@property(assign) MyClass *myObject;"相識
// 不同的地方在於,如果MyClass的例項析構後,這個屬性變數的值變成nil,而不是一個野指標,
 
@property(weak) MyClass *myObject;

Variable Qualifiers

You use the following lifetime qualifiers for variables just like you would, say, const.

__strong
__weak
__unsafe_unretained
__autoreleasing

__strong is the default. __weak specifies a zeroing weak reference to an object. __unsafe_unretained specifies weak reference to an object that is not zeroing—if the object it references is deallocated, the pointer is left dangling. You use__autoreleasing to denote arguments that are passed by reference (id *) and are autoreleased on return.

Take care when using __weak variables on the stack. Consider the following example:

NSString __weak *string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];
NSLog(@"string: %@", string);

Although string is used after the initial assignment, there is no other strong reference to the string object at the time of assignment; it is therefore immediately deallocated. The log statement shows that string has a null value.

You also need to take care with objects passed by reference. The following code will work:

NSError *error = nil;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
    // Report the error.
    // ...

 

However, the error declaration is implicitly:

NSError * __strong e = nil;

and the method declaration would typically be:

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

The compiler therefore rewrites the code:

NSError __strong *error = nil;
NSError __autoreleasing *tmp = error;
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;
if (!OK) {
    // Report the error.
    // ...

The mismatch between the local variable declaration (__strong) and the parameter (__autoreleasing) causes the compiler to create the temporary variable. You can get the original pointer by declaring the parameter id __strong * when you take the address of a __strong variable. Alternatively you can declare the variable as __autoreleasing.

Use Lifetime Qualifiers to Avoid Strong Reference Cycles

You can use lifetime qualifiers to avoid strong reference cycles. For example, typically if you have a graph of objects arranged in a parent-child hierarchy and parents need to refer to their children and vice versa, then you make the parent-to-child relationship strong and the child-to-parent relationship weak. Other situations may be more subtle, particularly when they involve block objects.

In manual reference counting mode, __block id x; has the effect of not retaining x. In ARC mode, __block id x; defaults to retaining x (just like all other values). To get the manual reference counting mode behavior under ARC, you could use__unsafe_unretained __block id x;. As the name __unsafe_unretained implies, however, having a non-retained variable is dangerous (because it can dangle) and is therefore discouraged. Two better options are to either use __weak (if you don’t need to support iOS 4 or OS X v10.6), or set the __block value to nil to break the retain cycle.

The following code fragment illustrates this issue using a pattern that is sometimes used in manual reference counting.

MyViewController *myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler =  ^(NSInteger result) {
   [myController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{
   [myController release];
}];

As described, instead, you can use a __block qualifier and set the myController variable to nil in the completion handler:

__block MyViewController *myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler =  ^(NSInteger result) {
    [myController dismissViewControllerAnimated:YES completion:nil];
    myController = nil;
};

Alternatively, you can use a temporary __weak variable. The following example illustrates a simple implementation:

MyViewController *myController = [[MyViewController alloc] init…];
// ...
__weak MyViewController *weakMyViewController = myController;
myController.completionHandler =  ^(NSInteger result) {
    [weakMyViewController dismissViewControllerAnimated:YES completion:nil];
};

For non-trivial cycles, however, you should use:

MyViewController *myController = [[MyViewController alloc] init…];
// ...
__weak MyViewController *weakMyController = myController;
myController.completionHandler =  ^(NSInteger result) {
    MyViewController *strongMyController = weakMyController;
    if (strongMyController) {
        // ...
        [strongMyController dismissViewControllerAnimated:YES completion:nil];
        // ...
    }
    else {
        // Probably nothing...
    }
};

In some cases you can use __unsafe_unretained if the class isn’t __weak compatible. This can, however, become impractical for nontrivial cycles because it can be hard or impossible to validate that the __unsafe_unretained pointer is still valid and still points to the same object in question.

自動釋放池

使用ARC,你不能使用NSAutoReleasePool類來管理自動釋放池了,作為替代,ARC使用一個新的語法結構:

@autoreleasepool {
     // Code, such as a loop that creates a large number of temporary objects.
}

編譯器根據工程是否使用ARC,來決定這個語法結構最終呈現方式,這個語法結構使用了一種比NSAutoReleasePool更高效的方式。

Outlets

The patterns for declaring outlets in iOS and OS X change with ARC and become consistent across both platforms. The pattern you should typically adopt is: outlets should be weak, except for those from File’s Owner to top-level objects in a nib file (or a storyboard scene) which should be strong.

Outlets that you create should will therefore generally be weak by default:

  • Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.

  • The strong outlets are frequently specified by framework classes (for example, UIViewController’s view outlet, or NSWindowController’s window outlet).

For example:

@interface MyFilesOwnerClass : SuperClass
 
@property (weak) IBOutlet MyView *viewContainerSubview;
@property (strong) IBOutlet MyOtherClass *topLevelObject;
@end

In cases where you cannot create a weak reference to an instance of a particular class (such as NSTextView), you should use assign rather than weak:

@property (assign) IBOutlet NSTextView *textView;

This pattern extends to references from a container view to its subviews where you have to consider the internal consistency of your object graph. For example, in the case of a table view cell, outlets to specific subviews should again typically beweak. If a table view contains an image view and a text view, then these remain valid so long as they are subviews of the table view cell itself.

Outlets should be changed to strong when the outlet should be considered to own the referenced object:

  • As indicated previously, this often the case with File’s Owner: top level objects in a nib file are frequently considered to be owned by the File’s Owner.

  • You may in some situations need an object from a nib file to exist outside of its original container. For example, you might have an outlet for a view that can be temporarily removed from its initial view hierarchy and must therefore be maintained independently.

其他的新功能

使用ARC技術,可以使得在棧上分配的指標隱式的初始化為nil,比如

- (void)myMethod {
    NSString *name;
    NSLog(@"name: %@", name);
}

上面的程式碼會Log出來一個null,不會象不使用ARC技術的時候使得程式崩潰。

相關文章