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既不strong
或weak
,它們是什麼,他們只是指針(在ARC是稱爲__unsafe_unretained
,此處的關鍵字是不安全)。
所以,如果你有直接使用ivars的代碼,並且沒有使用setter/getters的屬性來訪問它們,那麼從非ARC到ARC的切換可能導致代碼中的保留循環過去具有理智的內存管理。另一方面,從ARC移動到非ARC,類似這樣的代碼會導致懸掛指針(指向前一個對象的指針,但由於對象已經死亡,這些指向無處並且使用它們具有不可預知的結果),因爲過去的對象在現在可能意外死亡之前保持活力。
這似乎是共識。謝謝你的幫助。 – TomSwift