iOS開發(fā)之核心優(yōu)化——內(nèi)存管理
1. 內(nèi)存消耗
iPhone 和 iPad 設(shè)備的內(nèi)存資源非常有限向楼。如果某個應(yīng)用的內(nèi)存使用量超過了單個進程的上限嗜闻,那么它就會被操作系統(tǒng)終止使用。正是由于這個原因胁镐,成功的內(nèi)存管理在 iOS 應(yīng)用的實現(xiàn)過程中扮演著核心的角色闽寡。應(yīng)用中的內(nèi)存消耗分為兩部分:棧大小和堆大小吠冤。
1.1 棧大小
應(yīng)用中新創(chuàng)建的每個線程都有專用的椀缫ィ空間秽梅,該空間由保留的內(nèi)存和初始提交的內(nèi)存組成。椊宋可以在線程存在期間自由使用企垦。線程的最大棧空間很小晒来,這就決定了以下的限制
- 可被遞歸調(diào)用的最大方法數(shù):每個方法都有其自己的棧幀钞诡,并會消耗整體的棧空間湃崩。
例如:如果你調(diào)用方法 main荧降,那么 main 將調(diào)用 method1裕寨,而 method1 又將調(diào)用 method2裂允,這就存在三個棧幀了,且每個棧幀都會消耗一定字節(jié)的內(nèi)存 - 一個方法中最多可以使用的變量個數(shù):所有的變量都會載入方法的棧幀中困肩,并消耗一定的椪埃空間拗窃。
- 視圖層級中可以嵌入的最大視圖深度:渲染復(fù)合視圖將在整個視圖層級樹中遞歸地調(diào)用 layoutSubViews 和 drawRect 方法瞎领。如果層級過深泌辫,可能會導(dǎo)致棧溢出。
1.2 堆大小
每個進程的所有線程共享同一個堆九默。一個應(yīng)用可以使用的堆大小通常遠遠小于設(shè)備的 RAM 值震放。保持應(yīng)用的內(nèi)存需求總是處于 RAM 的較低占比是一個非常好的主意。雖然沒有強制規(guī)定驼修,但強烈建議使用量不要超過 80%~85%殿遂,要給操作系統(tǒng)的核心服務(wù)留下足夠多的內(nèi)存。不要忽視 didReceiveMemoryWarning 信號乙各。
2. 內(nèi)存管理模型
內(nèi)存管理模型基于持有關(guān)系的概念墨礁。
如果一個對象正處于被持有狀態(tài),那它占用的內(nèi)存就不能被回收耳峦。
- 當(dāng)一個對象創(chuàng)建于某個方法的內(nèi)部時恩静,那該方法就持有這個對象了。
- 如果這個對象從方法返回,則調(diào)用者聲稱建立了持有關(guān)系驶乾。這個值可以賦值給其他變量邑飒,對應(yīng)的變量同樣會聲稱建立了持有關(guān)系。
- 一旦與某個對象相關(guān)的任務(wù)全部完成级乐,那么就是放棄了持有關(guān)系疙咸。
- 這一過程沒有轉(zhuǎn)移持有關(guān)系,而是分別增加或減少了持有者的數(shù)量风科。當(dāng)持有者的數(shù)量降為零時撒轮,對象會被釋放,相關(guān)的內(nèi)存會被回收贼穆。這種持有關(guān)系計數(shù)通常被正式稱為引用計數(shù)腔召。當(dāng)你親自管理時,它被稱為手動引用計數(shù)(manual reference counting扮惦,MRC)臀蛛。雖然現(xiàn)在已經(jīng)十分罕見,但 MRC 對理解問題很有幫助⊙旅郏現(xiàn)如今的應(yīng)用大都使用自動引用計數(shù)(automatic reference counting 浊仆,ARC)
內(nèi)存管理規(guī)則
- 你擁有所有自己創(chuàng)建的對象,如 new豫领、alloc抡柿、copy 或 mutableCopy。
- 你可以用 MRC 中的 retain 或者 ARC 中的 __strong 引用來擁有任何對象的持有關(guān)系等恐。
- 在 MRC 中洲劣,當(dāng)不再需要某個對象時,你必須立即使用 release 方法來放棄對該對象的持有關(guān)系课蔬。而在 ARC 中則無需任何特殊操作囱稽。持有關(guān)系會在對象失去最后的引用(如
方法中的最后一行代碼)時被拋棄。 - 一定不能拋棄原本并不存在持有關(guān)系的對象二跋。
避免循環(huán)引用的規(guī)則
- 對象不應(yīng)該持有它的父對象战惊,應(yīng)該用 weak 引用指向它的父對象
- 作為必然的結(jié)果,一個層級體系中的子對象應(yīng)該保留祖先對象扎即。
- 連接對象不應(yīng)持有它們的目標對象吞获。目標對象的角色是持有者。連接對象包括以下幾種:
(1) 使用委托的對象谚鄙。委托應(yīng)該被當(dāng)作目標對象各拷,即持有者。
(2) 包含目標和 action 的對象闷营,這是由上一條規(guī)則推理得到的烤黍。例如, UIButton 會調(diào)用它的目標對象上的 action 方法。按鈕不應(yīng)該保留它的目標蚊荣。
(3) 觀察者模式中被觀察的對象初狰。觀察者就是持有者,并會觀察發(fā)生在被觀察對象上的變化互例。 - 使用專用的銷毀方法中斷循環(huán)引用奢入。
雙向鏈表中存在循環(huán)引用,環(huán)形鏈表中也存在循環(huán)引用媳叨。
在這類情況下腥光,一旦明確對象不會再被使用時(當(dāng)鏈表的表頭超出作用范圍),你要編寫代碼以打破鏈表的鏈接糊秆。