2012-06-13 85 views
5

除了在通過第三方代碼強制進入項目時處理它,我還沒有使用過ARC。我讀過所有的ARC文檔,但沒有看到這個問題的答案:ARC,非ARC和繼承

如果我有一個在-fobjc-arc編譯的模塊中定義的類,我可以從一個模塊中派生出一個新類不支持ARC?

在我看來,只要派生類不嘗試觸摸根類中的任何ivars,它應該可以正常工作。在我看來,即使有一個調用[super dealloc]的dealloc方法在派生類中也可以。

而且,另一種方式呢?我可以從非ARC類派生一個啓用ARC的類嗎?應該工作得很好,對嗎?

加分:混合ARC和非ARC代碼時,我應該讓自己意識到哪些問題?

回答

6

我沒有意識到任何問題。你必須認識到ARC就像是一個源代碼預處理器,在編譯過程中爲你添加內存管理調用。當你到達鏈接階段時,你不能真正告訴非ARC代碼的ARC代碼。 (這可能是一種過度簡化,但應該適合您的目的。)如果您的派生類具有正確的內存管理,並且超類具有正確的內存管理,則結果將正常工作。

關於我能想到的唯一區別是處理weak屬性。但是我不太瞭解那些可以使用ARC和MRC代碼與弱屬性的組合來得到錯誤代碼的人。

+0

這似乎是共識。謝謝你的幫助。 – TomSwift

0

這是一條評論,但考慮過了,我想擴展它所說的內容。

您是否試過從普通子類繼承ARC類?我的想法(沒有嘗試過)是,這是行不通的。首先,如果ARC類具有公共屬性或使用ARC關鍵字的ivars,例如weak,我認爲在頭文件編譯期間您會遇到錯誤。其次,我不知道dealloc會如何工作。你需要撥打[super dealloc]嗎?我不知道。

無論如何,如果你的超類是ARC,你爲什麼不在任何子類中使用ARC?這樣做根本沒有好處。

我可以從非ARC類派生一個支持ARC的類嗎?應該工作得很好,對嗎?

我想說這也行不通,但我會錯的。幾乎所有的東西都必須從手動引用計數的NSObject繼承。

+0

從MRC代碼中啓用ARC的類派生出來是有道理的,例如,當您使用ARC進行鏈接並且不能重寫主項目時。 (我這樣做,它似乎工作。)你不能在ARC下調用'[super dealloc]',我認爲編譯器會爲你插入調用。 – zoul

+0

@zoul - 是的,這也是我們的情況;第三方模塊需要ARC,但我們不準備將ARC用於我們自己的模塊。 – TomSwift

+0

擁有ARC類的非ARC子類也是有意義的。特別是,如果一個子類必須大量使用CF API,那麼從MRR代碼比ARC代碼更容易實現。 – bbum

0

是的,你既可以實現ARC父類的非ARC祖先,也可以實現非ARC父類的ARC祖先。實際上,ARC是一種語法糖,或者你可以說,它只是預處理器,它在編譯步驟中分析你的源代碼,並向你的代碼插入適當的[釋放]和[保留]調用。在運行時級別上,沒有任何更改(weak屬性除外)。

0

ARC意味着編譯器負責內存管理,非ARC意味着你照顧好它,但在這兩種情況下,內存管理的工作方式完全相同:

  • 如果一個對象必須活下去,其保留計數器增加(這是retain一樣)
  • 如果不再需要一個對象,它是失去了保留計數器被引用之前下降(這就是release一樣)
  • 如果您正在使用對象來完成,但它一定不會死亡,例如因爲您需要將其作爲方法結果返回(並且您不想返回死對象),所以它必須添加到autorelease池中,以後將減少其保留計數(這就是autorelease所做的,就像說「在將來某個時間在該對象上調用release」。)
  • 新創建的對象的保留計數爲1
  • 如果保留計數達到零,則釋放該對象。

無論你自己做了一切還是編譯器爲你做,它都不起作用。編譯之後,這些方法也被稱爲ARC,但使用ARC時,編譯器會爲您決定調用哪種方法。還有一些額外的魔法,例如在將方法返回給方法結果時,ARC並不總是必須添加對象到自動釋放池,這通常可以被優化掉,但是你不需要關心,因爲只有當調用者和被調用方法都使用弧;如果其中一個不是,那麼使用正常的autorelease(它仍然像以前一樣在ARC中工作)。

您唯一需要注意的就是保留週期。無論您是否使用ARC,引用計數都無法處理保留週期。這裏沒有區別。

陷阱?小心免費橋接。一個NSString *和一個CFStringRef事實上是同樣的事情,但ARC不知道CF世界,所以雖然ARC負責NSString,但您必須照顧CFString。使用ARC時,您需要告訴ARC如何橋接。上述

CFStringRef cfstr = ...; 
NSString * nsstr = (__bridge_transfer NSString *)cfstr; 
// NSString * nsstr = [(NSString *)cfstr autorelease]; 

代碼表示「ARC,請採取CFString對象的所有權,並採取儘快釋放它,你用它做的關懷」。代碼的行爲與下面註釋中顯示的代碼相似;所以小心,cfstr應該至少有一個保留計數,ARC將至少釋放一次,但尚未。反過來想:以上

NSString * nsstr = ...; 
CFStringRef cfstr = (__bridge_retained CFStringRef)cftr; 
// CFStringRef cfstr = (CFStringRef)[nsstr retain]; 

代碼表示「ARC,請給我NSString的所有權,我要發佈一次,我用它做的關懷」。當然,你必須遵守這個承諾!在某些時候,您必須致電CFRelease(cfstr)否則您將泄漏內存。

最後有(__bridge ...)這只是一個類型轉換,沒有所有權轉移。這種類型的轉換是危險的,因爲如果你試圖保持轉換結果,它可以創建懸掛指針。通常,在將ARC對象提供給期望CF對象的函數時使用它,因爲ARC將確保該對象保持活動直到函數返回,例如,這始終是安全的:

doSomethingWithString((__bridge CFStringRef)nsstr); 

即使ARC被允許在任何時間釋放nsstr作爲線下無碼有史以來訪問它了,它肯定不會釋放它之前這個函數返回和功能參數是由定義只能保證在函數返回前一直保持活動狀態(如果函數想讓字符串保持活動狀態,它必須保留它,然後ARC在釋放它之後不會釋放它,因爲保留計數不會變爲零)。

大多數人似乎與正在通過ARC鬥爭的事情對象爲void *背景下,作爲你有時不得不用舊的API,但是這實際上是在死的簡單:

- (void)doIt { 
    NSDictionary myCallbackContext = ...; 
    [obj doSomethingWithCallbackSelector:@selector(iAmDone:) 
     context:(__bridge_retained void *)myCallbackContext 
    ]; 
    // Bridge cast above makes sure that ARC won't kill 
    // myCallbackContext prior to returning from this method. 
    // Think of: 
    // [obj doSomethingWithCallbackSelector:@selector(iAmDone:) 
    // context:(void *)[myCallbackContext retain] 
    // ]; 
} 

// ... 

- (void)iAmDone:(void *)context { 
    NSDictionary * contextDict = (__bridge_transfer NSDictionary *)context; 
    // Use contextDict as you you like, ARC will release it 
    // prior to returning from this method. Think of: 
    // NSDictionary * contextDict = [(NSDictionary *)context autorelease]; 
} 

我必須真正的大對你一見鍾情,一見不見。請考慮以下代碼:

@implementation SomeObject { 
    id _someIVAR; 
} 

- (void)someMethod { 
    id someValue = ...; 
    _someIVAR = someValue; 
} 

此代碼在ARC和非ARC中不相同。在ARC所有的變量都默認很強,所以在ARC這段代碼的行爲就像這段代碼有:

@interface SomeObject 
    @property (retain,nonatomic) id someIVAR; 
@end 

@implementation SomeObject 

- (void)someMethod { 
    id someValue = ...; 
    self.someIVAR = someValue; 
} 

分配someValue將保留它,對象保持活着!在非ARC的代碼會像這樣:

@interface SomeObject 
    @property (assign,nonatomic) id someIVAR; 
@end 

@implementation SomeObject 

- (void)someMethod { 
    id someValue = ...; 
    self.someIVAR = someValue; 
} 

注意的屬性是不同的,因爲伊娃的非ARC既不strongweak,它們是什麼,他們只是指針(在ARC是稱爲__unsafe_unretained,此處的關鍵字是不安全)。

所以,如果你有直接使用ivars的代碼,並且沒有使用setter/getters的屬性來訪問它們,那麼從非ARC到ARC的切換可能導致代碼中的保留循環過去具有理智的內存管理。另一方面,從ARC移動到非ARC,類似這樣的代碼會導致懸掛指針(指向前一個對象的指針,但由於對象已經死亡,這些指向無處並且使用它們具有不可預知的結果),因爲過去的對象在現在可能意外死亡之前保持活力。