iOS内存管理编程指南
iOS下內存管理的基本思想就是引用計數,通過對象的引用計數來對內存對象的生命周期進行控制。具體到編程時間方面,主要有兩種方式:
1:MRR(manual retain-release),人工引用計數,對象的生成、銷毀、引用計數的變化都是由開發人員來完成。
2:ARC(Automatic Reference Counting),自動引用計數,只負責對象的生成,其他過程開發人員不再需要關心其銷毀,使用方式類似于垃圾回收,但其實質還是引用計數。
iOS不支持垃圾回收機制,這點與Mac OS有所不同。
ARC是Xcode 4.2之后加入的新特性,可能很多開發人員并不習慣使用,但使用ARC給開發帶來的便利是顯而易見的,鼓勵大家都去嘗試一下。
ARC的具體介紹及使用細則大家可以參考蘋果官方文檔,本文主要介紹MRR。
一:基本原則
關于MRR,我總結了一句話:是你的,就是你的;不是你的,就不是你的。
雖然看上去比較廢話,但揭示了MRR內存管理里的一個核心原則,“只負責你擁有的對象的生命周期”,也就是說,如果你對一個對象有所有權,那么你就要負責其回收的工作,否則,你不需要,也不能取回收你不擁有的對象。
那些對象屬于“擁有”范疇呢?
1:所有使用alloc, new, copy或mutabelCopy,以及這些關鍵詞開頭的函數返回的對象,你都是擁有所有權的,也就是要負責這些對象的內存回收工作。這是iOS開發中的一種約定,所以,當你編寫自己的alloc, new或copy類型的函數時,也請遵循這樣的命名規范。
2:retain返回的對象,擁有所有權。例如顯示調用retain函數返回的結果,或者synthesize 的retain類型的成員變量。
3:所有使用其他函數返回的對象,沒有所有權。
4:返回的對象的引用,沒有所有權。
5:autorelease返回的對象沒有所有權。
舉例說明:
?
[cpp]?view plaincopy二:成員變量的內存管理
?
注意上面的例子4,將成員變量聲明為retain并synthesize時,編譯器會幫你生成對應變量的setter與getter,其中, 生成的setter形式如下:
?
[cpp]?view plaincopy首先獲得newString的所有權,然后釋放掉已經擁有的_stringD(其實此對象有可能并沒有被釋放,取決與其release后的引用計數是否為0), 再將newString的值賦給_stringD,這樣,獲得了新的,釋放了舊的,就完成了對象所有權的釋放與獲得。注意retain與release的調用順序,避免了同一個對象賦值引起的懸空指針問題。
?
當成員變量聲明為assign和copy時,其生成的setter形式如下:
?
[cpp]?view plaincopycopy類型的成員變量也是擁有所有權的,也要在dealloc函數中顯式釋放。而assign不是。
?
很多人對self.stringD = @"abc"的調用形式比較困惑; 其實,編譯器會將此語句自動轉換為[self.setStringD:@"abc"];
還有一點要注意,聲明成員類型時只有copy,沒有mutableCopy,那么,如果一個成員變量是MutableArray,且被聲明成copy類型,但編譯器生成的setter中調用的是copy,而不是MutableCopy,所以,向MutableArray類型的成員插入數據時就會報錯,因為其保存的真正類型不是MutableArray,而是Array. 這種情況有兩個解決方案:一是將改變量類型聲明為retain形式; 二是手動編寫setter,調用mutableCopy函數。
NSObject函數聲明了dealloc函數來清理內存,所有有“retain, copy”類型成員變量的類都要實現這個函數。在其內,要調用相應對象的release函數,不要忘記在【最后】調用[super dealloc];
說到成員變量的聲明周期,還要提一下IBOutlet類型的變量,默認情況下, IBOutlet對象的類型都是retain的,由于這些對象來自界面文件(xib, storyboard),所以其出事化過程無需關心,其他處理方式與普通成員變量大體相同,只有一點,需要在- ?(void) viewDidUnload函數中將這些變量置空, 如下所示:
?
[cpp]?view plaincopy當出現內存警告時,viewDidUnload將會被調用。前面提到過,self.outletA = nil 等價于[self setOutletA: nil], 所以,在出現內存警告的情況下,IBOutlet類型的對象會被釋放。
?
三:容器對象與內存管理
iOS中,容器對象對其內的對象擁有所有權,也就是說,當一個對象被插入到容器內時,其retainCount會加一,從容器內移除時,其retainCount會減一,當容器本身被release時,期內所有對象的retainCount都會減一。如下代碼所示:
?
[cpp]?view plaincopy?
四:稀缺資源的管理
稀缺資源包括文件,網絡連接,緩存等,這些資源是很關鍵的系統資源,系統內其他應用可能會隨時需要這些資源,所以,這些資源就不適合作為類的成員變量了,因為dealloc的實際調用時間,是否真正調用是我們無法控制的,很有可能造成稀缺資源被無意義的占用,二其他應用卻無法獲得相應資源。所以,隨時申請隨時釋放是最好的選擇。
五:AutoRelease
簡單說,autorelease對象的釋放動作由AutoReleasePool完成,所有autorelease對象在其【對應】的AutoReleasePool釋放的過程中,都會受到一條release消息,也就是說,pool析構的實際也就是autorelease對象析構的時機,注意,這里的【對應】指的是離改autorelese最近的那一個AutoRelasePool。
有這么幾種情況必須自己創建AutoReleasePool:
1:程序沒有界面,也就是沒有消息循環的程序,
2:一個循環內創建大量臨時的autorelease對象,那么寫法最好是這樣的:
?
[cpp]?view plaincopy若果沒有循環中的pool, 那么直到結束循環之前,這1000000個autorelease 臨時對象都不會被釋放掉,占用大量內存。
?
3:線程函數內需要有AutoReleasePool對象,否則期內生成的autorelease對象在線程函數結束時不會被釋放(此條對Cocoa-Touch不適用)。
4:普通函數返回對象,這里的普通函數指的是非alloc, new, copy, mutablecopy開頭的函數,為了確保返回的對象有效,需要將返回的對象設為autorelease,如下所示:
?
[cpp]?view plaincopy實際上,iOS SDK中絕大多數普通函數返回的都是autorelease對象。
?
六:其他注意事項
1:避免循環引用,如果兩個對象互相為對方的成員變量,那么這兩個對象一定不能同時為retain,否則,兩個對象的dealloc函數形成死鎖,兩個對象都無法釋放。
2:不要濫用autorelease,如果一個對象的生命周期很清晰,那最好在結束使用后馬上調用release,過多的等待autorelease對象會給內存造成不必要的負擔。
3:編碼過程中,建議將內存相關的函數,如init, dealloc, viewdidload, viewdidunload等函數放在最前,這樣比較顯眼,忘記處理的概率也會降低。
4:AutoReleasePool對象在釋放的過程中,在IOS下,release和drain沒有區別。但為了一致性,還是推薦使用drain。
轉載于:https://www.cnblogs.com/songfeixiang/p/3733753.html
總結
以上是生活随笔為你收集整理的iOS内存管理编程指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DELPHI 禁止移动窗体
- 下一篇: http://www.csdn.net/