以下是我所總結的一些常見記憶體管理需要注意的方面,並沒有嚴格意義上的資料來源,可能有些部分有歧義,不太正確或遺漏,大家可以和我說說,共同進步。
按照這樣方式來使用的話,確實可以大大減少記憶體方面的問題,錯誤使用記憶體導致的崩潰也會少點。
這裡記錄下,方便以後參考,也為大家提供個借鑒。
1. 保持對象的屬性/成員變數和對象本身的引用計數保持一致
a) 成員變數初始化時,不應該用autorelease的,如果是autorelease,則最好要retain一下,保持引用計數為1,然後在dealloc中釋放。
b) 對於屬性,除自訂的setter方法和dealloc之外,都需要使用self.testProperty=@"testPropert"的方式來進行賦值,這樣可以保持自己控制的引用計數始終為1;否則則需要在每次賦值之前都進行release。如
[_testProperty release];
_testProperty = testProperty;
c) 對應於a),所有的成員變數都需要在dealloc中進行釋放。
d) 每次成員變數release之後,除非立馬進行賦值,否知需要將其置為nil,防止其他引用的地方無法對其進行判斷。
dealloc中可以酌情處理,關於成員變數,不設定為nil也無妨。簡單的處理方式可以將dealloc中所有的釋放的變數都置為nil。
e) 屬性中所有的obj-c對象都應該設定為retain(除delegate外),對其引用計數進行+1操作,而不要使用assign。
對於類似於BOOL,int,以及Core**架構等則需要使用assign,不需要更改引用計數。
但是delegate等需要使用assign來進行屬性聲明,當前對象不應該管理代理程式對象的引用計數。
如:
@property (nonatomic, copy) NSString*testProperty;
@property (assign) BOOLflag;
@property (assign) id<property>delegate;
f) 基本原則,誰分配,誰釋放。在和C架構互動使用時更應該注意。
g) 注意在struct中使用obj-c對象的記憶體釋放問題,確保在free的時候將引用計數變為0,或已經是autorelease。
2. 屬性關鍵字的使用
a) retain和copy的區別
retain的產生的程式碼類似於如下:
- (void)setTestProperty:(id)testProperty{ if(_testProperty != testPropert) { [_testProperty release]; _testProperty = [testPropert retain]; }}
所以retain會釋放舊的值,然後設定為新的值,並retain,自身保持其引用計數。從此也可以看出,成員變數_testProperty引用外部變數時,始終會retain一次。
copy會產生一個新的對象指標,例如
一個NSString*的地址為0x0011,內容為@"testProperty"
copy操作之後,產生一個新的NSString *,其地址為0x1122,內容相同,新的對象的retainCount為1,不對舊對象進行操作,故舊對象沒有任何變化。
所以可以理解為retain其實是對指標的拷貝,copy是對內容的拷貝。但是由於NSArray,NSDictionay對象等,由於其內容也是指標,故拷貝的其實是數組內部的指標。
也即一般情況下,都可以將NSString 的屬性聲明為copy的。如@property (nonatomic, copy) NSString *test;
b) assign,nonatomic
assign, Setter方法為直接賦值,不進行retain操作,通常是為了基本類型或像delegate之類的引用(防止出現循環參考問題)。
nonatomic, 非原子性訪問,不加同步,多線程並發訪問會提升效能,如果不加此屬性,預設為原子型事務。鎖被加到所屬對象執行個體級。nonatomic使用也較多。
c) @synthesize xxx; 為xxx產生相應的Setter/Getter方法
d) 系統會預設分配一個沒有對應變數的屬性,如下樣本:
@property (nonatomic, copy) NSString *testProperty;
@synthesize testProperty;
樣本中並未聲明任何變數對應與testProperty屬性,但是仍然可以使用self.testProperty來進行賦值和取值。
這是因為系統會預設分配一個_testProperty的變數來與之對應。但建議還是添加一個成員變數,這樣更為清晰。
3. 在obj-c編程中使用了CoreGraphics.framework,CoreTelephony.framework等C架構
該類C架構採用CFRelease/CFRetain去控制對象的生命週期。其可以與obj-c對象直接轉換。例如:
NSString *str = [NSStrig stringWithFormat:@"%@", @"abcdefg"];
CFStringRef ref = (NSString *)[str retain];
此時需要使用CFRelease(ref);來對其進行釋放;
4. 直接使用C語言,則需要自己去控制記憶體的分配和釋放
malloc和free需要嚴格對應,如果作為函數參數的話,需要使用指向指標的變數來作為函數參數。外部提供釋放。
關於這塊,建議看看聖經《深入理解電腦系統》,上面講得非常透徹與詳細。
5. 特殊情境
a) @"123", 以及initWithString等產生的引用計數都是非常大的。對此執行的release操作只是為了與之前的init對應,並不會真正的釋放
b) 對象release之後,不會立刻釋放,如:
[obj release];
NSLog(@"obj = %@", obj); // 這句話不會導致崩潰
NSLog(@"obj = %@", obj); // 此時的調用會導致崩潰
c)NSAutoreleasePool的效能問題
NSAutoreleasePool也會佔用一定的效能,在XCode4.2中預設使用的編譯期支援@autoreleasepool{}塊來代替NSAutoreleasePool,其效能相對於
NSAutoreleasePool來說,有較大的提升(記得曾經看過一份文檔說會提升6倍以上)。
by yytong