iOS  內存管理

Objective-C提供了三種內存管理方式:manual retain-release(MRR,手動管理),automatic reference counting(ARC,自動引用計數),garbage collection(垃圾回收)。iOS不支持垃圾回收;ARC作爲蘋果新提供的技術,蘋果推薦開發者使用ARC技術來管理內存;這篇筆記主要講的是手動管理。

內存管理的目的是:
1.不要釋放或者覆蓋還在使用的內存,這會引起程序崩潰;
2.釋放不再使用的內存,防止內存泄露。iOS程序的內存資源是寶貴的。

MRR手動管理內存也是基於引用計數的,只是需要開發者發消息給某塊內存(或者說是對象)來改變這塊內存的引用計數以實現內存管理(ARC技術則是編譯器代替開發者完成相應的工作)。一塊內存如果計數是零,也就是沒有使用者(owner),那麼objective-C的運行環境會自動回收這塊內存。

objective-C的內存管理遵守下面這個簡單的策略:
注:文檔中把引用計數加1的操作稱爲“擁有”(own,或者take ownership of)某塊對象/內存;把引用計數減1的操作稱爲放棄(relinquish)這塊對象/內存。擁有對象時,你可以放心地讀寫或者返回對象;當對象被所有人放棄時,objective-C的運行環境會回收這個對象。
1.你擁有你創建的對象
也就是說創建的對象(使用alloc,new,copy或者mutalbeCopy等方法)的初始引用計數是1。
2.給對象發送retain消息後,你擁有了這個對象
3.當你不需要使用該對象時,發送release或者autorelease消息放棄這個對象
4.不要對你不擁有的對象發送“放棄”的消息


注:簡單的賦值不會擁有某個對象。比如:
...
NSString *name = person.fullName;
...
上面這個賦值操作不會擁有這個對象(這僅僅是個指針賦值操作);這和C++語言裏的某些基於引用計數的類的行爲是有區別的。想擁有一個objective-C對象,必須發送“創建”或者retain消息給該對象。


dealloc方法
dealloc方法用來釋放這個對象所佔的內存(包括成員變量)和其它資源。
不要使用dealloc方法來管理稀缺資源,比如文件,網絡鏈接等。因爲由於bug或者程序意外退出,dealloc方法不能保證一定會被調用。


Accessor Methods和內存管理
Accessor Methods,也就是對象的property(屬性)的getter和setter方法。顯然,如果getter返回的對象已經被運行環境回收了,那麼這個getter的返回值是毫無意義的。這就需要在setter方法裏“擁有”相應的property。
比如:
@interface Counter : NSObject
@property (nonatomic, retain) NSNumber *count;
@end
getter方法僅僅返回成員變量就可以:
-(NSNumber *)count {
    return _count;
}
setter方法需要保證對這個成員變量的“擁有”:
-(void)setCount:(NSNumber *)newCount {
    [newCount retain]; //擁有新值
    [_count release]; //放棄老值
    _count = newCount; //簡單賦值
}

使用Accessor Methods
以下是一種使用方式:
...
NSNumber *zero = [NSNumber alloc] initWithInteger:0];
[self setCount:zero];
[zero release];
...
以下是一種可能引發錯誤的,偷懶的使用方式:
...
NSNumber *zero = [NSNumber alloc] initWithInteger:0];
[_count release];
_count = zero; //這個代碼做了不合理的假設-_count對象“擁有”了某個內存。當修飾count屬性的
                         //attribute發聲變化時,這個假設就不一定正確了。
...

不要在初始化方法(Initializer)和dealloc方法裏使用Accessor Methods
不要在初始化方法裏使用accessor methods的原因可能是(原文檔中沒有說明):在初始化方法裏,成員變量處於最初的狀態,並沒有任何值。考慮到一個成員變量的setter方法一般會對成員變量的舊值發送release消息。這種行爲在初始化方法裏沒有意義。
如果需要在Initializer裏給成員變量賦值,可參見一開始提到的原始文檔裏給出的示例代碼。


使用weak reference(弱引用)來避免retain cycle
對一個對象發送retain消息會創建對這個對象的強引用(strong reference)。如果兩個對象都有一個強引用指向對方,那麼就形成了一個環(retain cycle)。這個環使得這兩個對象都不可能被release。
弱引用(weak reference)指的是一種non-owning(非擁有)的關係,比如簡單指針賦值關係。使用弱引用避免了retain cycle。但是需要注意的是,弱引用不能保證弱引用指向的對象是否存在,所以發消息給這個對象時一定要小心。如果弱引用指向的對象已經釋放,那麼發送消息給它會導致程序崩潰。所以,需要一點點額外的操作來使用弱引用所指的對象。比如,當向notification center註冊一個對象時,notification center保存了一個指向這個對象的弱引用。當這個對象被回收時,需要通知下notification center。


當你使用對象時,要確保這個對象不會被回收。主要要注意以下兩種情形:
1.當一個對象從collection對象(collection指的數組之類的集合)移除時,如果這個僅被collection對象擁有,那麼移除操作了會被即可回收。所以如果要使用這個將要移除的對象,要先retain。
2.當“父”對象回收時。這和情形1類似。


Autorelease Pool
Autorelease Pool可以延後發送release消息給一個對象。發送一個autorelease消息給一個對象,相當於說這個對象在“一定時期”內都有效,“一定時期”後再release這個對象。
Autorelease Pool幾個要點:
-autorelease pool是一個NSAutoreleasePool對象。
-程序裏的所有autorelease pool是以桟(stack)的形式組織的。新創建的pool位於桟的最頂端。當發送autorelease消息給一個對象時,這個對象被加到棧頂的那個pool中。發送drain給一個pool時,這個pool裏所有對象都會受到release消息,而且如果這個pool不是位於棧頂,那麼位於這個pool“上端”的所有pool也會受到drain消息。
-一個對象被加到一個pool很多次,只要多次發送autorelease消息給這個對象就可以;同時,當這個pool被回收時,這個對象也會收到同樣多次release消息。簡單地可以認爲接收autorelease消息等同於:接收一個retain消息,同時加入到一個pool裏;這個pool用來存放這些暫緩回收的對象;一旦這個pool被回收(drain),那麼pool裏面的對象會收到同樣次數的release消息。
-UIKit框架已經幫你自動創建一個autorelease pool。大部分時候,你可以直接使用這個pool,不必自己創建;所以你給一個對象發送autorelease消息,那麼這個對象會加到這個UIKit自動創建的pool裏。某些時候,可能需要創建一個pool:
1.沒有使用UIKit框架或者其它內含autorelease pool的框架,那麼要使用pool,就要自己創建。
2.如果一個循環體要創建大量的臨時變量,那麼創建自己的pool可以減少程序佔用的內存峯值。(如果使用UIKit的pool,那麼這些臨時變量可能一直在這個pool裏,只要這個pool受到drain消息;完全不使用autorelease pool應該也是可以的,可能只是要發一些release消息給這些臨時變量,所以使用autorelease pool還是方便一些)
3.創建線程時必須創建這個線程自己的autorelease pool。
-使用alloc和init消息來創建pool,發送drain消息則表示這個pool不再使用。pool的創建和drain要在同一上下文中,比如循環體內。



轉自:at9009
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章