簡體   English   中英

ARC,非ARC和繼承

[英]ARC, non-ARC, and inheritance

當它通過第三方代碼將其強制進入項目時,除了處理它外,我還沒有使用過ARC。 我已經閱讀了所有ARC文檔,但沒有看到以下問題的答案:

如果我有一個用-fobjc-arc編譯的模塊中定義的類,是否可以在未啟用ARC的模塊中從中派生一個新類?

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

而且,反過來呢? 我可以從非ARC類派生支持ARC的類嗎? 也應該工作正常,對嗎?

優點:在混合ARC和非ARC代碼時,我應該意識到自己的陷阱嗎?

我沒有發現任何問題。 您必須意識到ARC就像源代碼預處理器一樣,它在編譯期間為您添加了內存管理調用。 進入鏈接階段時,您實際上無法從非ARC代碼中分辨出ARC代碼。 (這可能是一種過度簡化,但應該可以滿足您的目的。)如果派生類具有正確的內存管理,而父類具有正確的內存管理,則結果將正常工作。

我能想到的唯一區別是處理weak屬性。 但是我對這些還不十分了解,無法說出使用性能較弱的ARC和MRC代碼的某種組合來得出錯誤代碼的可能性。

這是一條評論,但是考慮到這一點,我想擴大它的意思。

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

無論如何,如果您的超類是ARC,為什么不在任何子類中使用ARC? 這樣做根本沒有優勢。

我可以從非ARC類派生支持ARC的類嗎? 也應該工作正常,對嗎?

我當時想說那也不行,但是我會錯的。 幾乎所有內容都必須從NSObject繼承,而NSObject是手動引用計數。

是的,您都可以實現ARC父類的非ARC祖先,也可以實現非ARC父類的ARC祖先。

實際上,ARC是一個語法糖,或者您可以說,它只是預處理器,它在編譯步驟分析您的源代碼,並在代碼中插入適當的[release]和[retain]調用。 在運行時級別不變( weak屬性除外)。

ARC表示編譯器負責內存管理,非ARC表示您負責內存管理,但是在兩種情況下,內存管理的工作方式完全相同:

  • 如果某個對象必須保持活動狀態,則其保留計數器會增加(這就是retain作用)
  • 如果不再需要某個對象,則在丟失對該對象的引用之前,它的保留計數器會減少(這就是release功能)
  • 如果完成了一個對象但它仍未死,例如,當您需要將其作為方法結果返回時(並且您不想返回死對象),則必須將其添加到將減少數量的自動釋放池中其保留計數在以后的時間(這就是autorelease功能,就像說“在將來某個時間對該對象的調用release ”。)
  • 新創建的對象的保留計數為1
  • 如果保留計數達到零,則釋放對象。

無論您是自己做所有事情,還是編譯器為您做這些,它都不起作用。 編譯后,也會使用ARC調用這些方法,但是使用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的保留數至少應為1,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將不會取消分配它,因為保留計數不會為零)。

就像您有時使用較舊的API時一樣,大多數人似乎在掙扎的是將ARC對象作為void *上下文傳遞,但這實際上非常簡單:

- (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中的ivar既不是strong也不是weak ,它們什么也不是,它們只是指針(在ARC中稱為__unsafe_unretained ,此處的關鍵字為unsafe )。

因此,如果您的代碼直接使用ivars,並且不使用帶有setters / getters的屬性來訪問它們,那么從非ARC切換到ARC可能會導致以前具有健全內存管理的代碼保持周期。 另一方面,從ARC移到非ARC時,像這樣的代碼可能導致懸空的指針(指向以前對象的指針,但由於對象已經死亡,因此指向無處,使用它們會產生不可預測的結果),就像過去的對象被活着之前可能現在意外死亡。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM