iOS_使用ARC需要注意的問題

來源:互聯網
上載者:User

記憶體管理基本原則
記憶體管理的依循下面的基本原則


自己產生的對象,那麼既是其持有人
不是自己產生的對象,也可成為其持有人(一個對象可以被多個人持有)
如果不想持有對象的時候,必須釋放其所有權
不能釋放已不再持有所有權的對象
不管ARC有沒有效,該原則始終存在。

所有權關鍵字
從代碼上看,有ARC的代碼和沒有ARC的代碼區別就在下面的幾個關鍵字。

類似 NSObject* 的物件類型,或者 id 類型1,當ARC有效時候,根據具體情況,這些關鍵字必須要使用2。

__strong
__weak
__unsafe_unretained
__autoreleasing
__strong是預設的修飾符。

__weak修飾了一個自動nil的weak引用。

__unsafe_unretained聲明了一個不會自動nil的weak引用。當變數被釋放,那麼它就變成了一個野指標了。

__autoreleasing 用來修飾一個聲明為 (id *) 的函數的參數,當函數傳回值時被釋放。

接下來,我們結合下面ARC的使用準則,來看看一些使用ARC後的技術細節。

ARC使用準則
為了比秒程式秒退的尷尬,ARC有效時,我們的代碼必須遵循下面的準則。

不能使用 retain/release/retainCount/autorelease
不能使用 NSAllocateObject/NSDeallocateObject
不能使用 NSZone
不能明示調用dealloc
記憶體管理相關的函數必須遵循命名規則
使用@autoreleasepool代替NSAutoreleasePool
Objective-C 對象不能作為C語言結構體(struct/union)的成員
【id】與【void*】之間需要明示cast
建議使用Objective-C的class來管理資料格式,來代替C語言的struct。不能隱式轉換 id 和 void *。

讓我們一個一個來分析


不能使用 retain/release/retainCount/autorelease
記憶體管理完全交給編譯器去做,所以之前記憶體相關的函數(retain/release/retainCount/autorelease)不能出現在程式中。Apple的ARC文檔中也有下面的說明。

ARC 有效後,不需要再次使用retain 和 release

如果我們在程式中使用這些函數,經得到類似下面的編譯錯誤資訊。

    error: ARC forbids explicit message send of ’release’
         [o release];
          ^ ~~~~~~~不能使用 NSAllocateObject/NSDeallocateObject
產生並持有一個Objective-C對象的時候,往往像下面一樣使用NSObject的alloc介面函數。

    id obj = [NSObject alloc];實際上,如果我們看了GNUstep 中關於 alloc 的代碼就會明白,實際他是使用 NSAllocateObject 來產生並持有對象執行個體的。換言之,ARC有效時候,NSAllocateObject函數的調用也是禁止的。如果使用,也會遇到下面的編譯錯誤。

    error: ’NSAllocateObject’ is unavailable:
        not available in automatic reference counting mode同樣,對象釋放時使用的 NSDeallocateObject 函數也不能使用。

不能使用 NSZone
NSZone 是什嗎?NSZone 是為了防止記憶體片段而匯入的一項措施。Zone 是記憶體管理的基本單元,系統中管理複數的Zone。系統根據對象的使用目的,尺寸,分配其所屬的Zone地區。以提高對象的訪問效率,避免不必要的記憶體片段。但是,現在的運行時系統(用編譯開關 __OBJC2__ 指定的情況下)是不支援Zone概念的。所以,不管ARC是否有效,都不能使用 NSZone。

不能明示調用dealloc
不管是否使用ARC,當對象被釋放的時候,對象的dealloc函數被調用(就像是C++中對象的解構函式)。在該函數中,需要做一些記憶體釋放的動作。比如,當對象中使用了malloc分配的C語言記憶體空間,那麼dealloc中就需要像下面一樣處理記憶體的釋放。

1
2
3
4 - (void) dealloc
{
    free(buffer_);
}


又或者是註冊的delegate對象,觀察者對象需要被刪除的時候,也是在dealloc函數中動作。

1
2
3
4 - (void) dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


如果在ARC無效的時候,我們還要像下面一樣,調用父類對象的dealloc函數。

1
2
3
4 - (void) dealloc
{
    [super dealloc];
}


但是當ARC有效時候,[super dealloc];的調用已經被編譯器自動執行,已經不需要我們明示調用了。如果你在代碼中還這樣寫,難免遇到下面的錯誤。

    error: ARC forbids explicit message send of ’dealloc’
         [super dealloc];
          ^ ~~~~~~~記憶體管理相關的函數必須遵循命名規則
在iPhone開發之深入淺出 (3) — ARC之前世今生中,我們知道如果是 alloc/new/copy/mutableCopy/init 開頭的函數,需要將對象所有權返回給調用端。這條規則不管ARC是否有效都應該被遵守。只是 init 開頭的函數比較特殊,他只在ARC下有要求,而且異常苛刻。

init 開始的函數只能返回id型,或者是該函數所屬的類/父類的物件類型。基本上來說,init函數是針對alloc函數的傳回值,做一些初始化處理,然後再將該對象返回。比如:

    id obj = [[NSObject alloc] init];再比如下面定義的函數就是不對的:

    - (void) initThisObject;需要是下面這樣:

    - (id) initWithObject:(id)obj;另外,下面名為 initialize 的函數比較特殊,編譯器將把它過濾掉,不按上面的規則處理。

使用@autoreleasepool代替NSAutoreleasePool
在ARC之下,已經不能在代碼中使用 NSAutoreleasePool,我們之前寫 main.m 檔案的時候,往往像下面這樣寫。

1
2
3
4
5
6     int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}


而當ARC有效後,我們需要用@autoreleasepool代替NSAutoreleasePool。

1
2
3
4
5
6 int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}


當編譯器看到 @autoreleasepool 定義的塊後會自動產生 NSAutoreleasePool 對象,並將需要的對象放入 AutoReleasePool 中,當出方塊的定義範圍時,pool 中的對象將被釋放。

Objective-C 對象不能作為C語言結構體(struct/union)的成員
當我們設定ARC有效,並在C語言的結構體中定義Objective-C的對象時,將出現類似下面的編譯錯誤。

1
2
3 struct Data {
    NSMutableArray *array;
};


    error: ARC forbids Objective-C objs in structs or unions
         NSMutableArray *array;
                         ^由於 ARC 是將記憶體管理的細節委託給編譯器來做,所以說編譯器必須要管理對象的生命週期。而LLVM 3.0中不存在對單純C語言構造體成員的記憶體管理方法。如果單純是棧對象,利用進出棧原理,可以簡單地維護對象的生命週期;而結構體是不行的,簡單地理解,結構體沒有解構函式,編譯器自身不能自動釋放其內部的 Objective-C 對象。

當我們必須在C語言的結構體中放入 Objective-C 對象的時候,可以使用 void* 轉型,或者使用 __unsafe_unretained 關鍵字。比如下面:

1
2
3 struct Data {
    NSMutableArray __unsafe_unretained *array;
};


這樣一來,該記憶體資訊不在編譯器記憶體管理對象內,僅僅是使用而已,沒有對象的持有權。當然,對象所有權的持有人需要明確的管理他與該結構體的互動,不要引起不必要的錯誤3。

【id】與【void*】之間需要明示cast
ARC 有效時候,由於編譯器幫我們做了記憶體管理的工作,所以我們不需要太擔心。但是當與 ARC 管理以外的物件類型互動的時候,就需要特殊的轉型關鍵字,來決定所有權的歸屬問題。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.