[英]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.