作者:Thomas Hanning宴凉,原文鏈接,原文日期:2015/08/06
譯者:BridgeQ恬口;校對(duì):lfb-CD校读;定稿:shanks
近年來(lái),移動(dòng)設(shè)備的性能越來(lái)越強(qiáng)大祖能。然而歉秫,同桌面電腦相比,性能上總還是有一段不小的差距养铸。同時(shí)雁芙,用戶界面和交互設(shè)計(jì)的要求也越來(lái)越高。所以钞螟,為移動(dòng)設(shè)備編寫內(nèi)存高效的應(yīng)用仍然很有必要兔甘。
什么是內(nèi)存高效的應(yīng)用
通俗點(diǎn)講,內(nèi)存高效的應(yīng)用是指:僅使用必要的內(nèi)存消耗并盡量減少內(nèi)存消耗鳞滨;用戶界面設(shè)計(jì)使用低內(nèi)存消耗的框架裂明。當(dāng)然,一個(gè)復(fù)雜度更高的應(yīng)用也肯定需要更高的內(nèi)存消耗。
接下來(lái)我們首先來(lái)回顧一小段歷史:
自動(dòng)引用計(jì)數(shù)(ARC:Automatic Reference Counting)
在早期的 iOS 開發(fā)中闽晦,內(nèi)存管理扮演著非常重要的角色。因?yàn)閭鹘y(tǒng)的垃圾回收機(jī)制對(duì)于移動(dòng)平臺(tái)來(lái)說(shuō)是非常低效的提岔,所以蘋果把內(nèi)存管理的責(zé)任交給了開發(fā)者仙蛉,你需要通過(guò)手動(dòng)的方式來(lái)增加或減少一個(gè)對(duì)象的引用計(jì)數(shù)。
通過(guò)這種方式碱蒙,你可以寫出內(nèi)存管理非常高效的應(yīng)用荠瘪,因?yàn)閷?duì)象不再使用時(shí)就立刻被銷毀了。但另一方面赛惩,很多時(shí)候手動(dòng)管理內(nèi)存并不容易哀墓,也經(jīng)常產(chǎn)生一些不易被發(fā)現(xiàn)的bug。所以喷兼,這并不是解決內(nèi)存管理問題的最好辦法篮绰。
新的解決方案是在 iOS 5 中被提出的:自動(dòng)引用計(jì)數(shù)(ARC)。從此之后季惯,控制引用計(jì)數(shù)的命令會(huì)在編譯期間被自動(dòng)加入而無(wú)需手動(dòng)編寫吠各。這樣帶來(lái)的好處是:一方面能編寫出內(nèi)存高效的代碼,另一方面讓開發(fā)者不用再關(guān)心內(nèi)存管理問題勉抓。這個(gè)解決方案非常棒贾漏,以至于 Mac OS X 的應(yīng)用程序也開始使用 ARC 來(lái)管理內(nèi)存。
不過(guò)藕筋,盡管你不需要再關(guān)心引用計(jì)數(shù)了纵散,但還是需要你去關(guān)心一些其他內(nèi)存管理相關(guān)的知識(shí)點(diǎn)。
選擇合適的部署版本
正如前文所說(shuō)隐圾,不同的需求意味著應(yīng)用的內(nèi)存消耗也不盡相同伍掀,除此之外,不同的需求還意味著應(yīng)用適應(yīng)于不同的部署版本(應(yīng)用運(yùn)行所支持的最低系統(tǒng)版本)翎承。舉個(gè)栗子硕盹,如果你的部署版本是 iOS 5,那么你就不能忘了要支持第一代 iPad 產(chǎn)品叨咖,它的內(nèi)存大小僅僅是 256 MB瘩例。雖然,支持盡可能多的版本是一種不錯(cuò)的選擇甸各,但是垛贤,在你所支持的版本上為用戶帶來(lái)良好的用戶體驗(yàn)才是更重要的。所以趣倾,當(dāng)你想支持一些舊設(shè)備的時(shí)候聘惦,在設(shè)計(jì)階段就要仔細(xì)考慮內(nèi)存消耗問題。
下面列舉了不同系統(tǒng)版本所支持的一些舊設(shè)備:
- iOS 9: iPhone 4S / iPad 2 / iPad Mini 1
- iOS 8: iPhone 4S / iPad 2 / iPad Mini 1
- iOS 7: iPhone 4 / iPad 2 / iPad Mini 1
- iOS 6: iPhone 3GS / iPad 2/ iPad Mini 1
- iOS 5: iPhone 3GS / iPad 1 / –
由于蘋果的生態(tài)系統(tǒng)更新速度比較快儒恋,所以支持最新的兩代操作系統(tǒng)版本是一個(gè)很好的選擇善绎。除了內(nèi)存方面的問題黔漂,支持過(guò)多的系統(tǒng)版本還會(huì)帶來(lái)開發(fā)和測(cè)試等諸多方面的問題。
圖片資源
在移動(dòng)應(yīng)用中禀酱,圖片是非常重要的資源炬守。然而,圖片也通常代表著高內(nèi)存消耗剂跟。在處理圖片資源的時(shí)候减途,有以下兩點(diǎn)需要注意:
- 首先,圖片應(yīng)該有合適的尺寸曹洽。如果你有一個(gè)表格視圖鳍置,上面需要 100 × 100 像素的圖片,而你卻使用 1000 × 1000 像素的圖片送淆,這一定是一個(gè)非常糟糕的作法税产,性能會(huì)受到非常大的影響。如果你是通過(guò)請(qǐng)求服務(wù)器獲取的圖片坊夫,那么服務(wù)器也應(yīng)該進(jìn)行處理以提供合適尺寸的圖片砖第。
- 其次,一定要保證圖片只是在需要使用的時(shí)候才被加載环凿。舉個(gè)栗子梧兼,表格視圖里的圖片只有當(dāng)單元格顯示出來(lái)的時(shí)候才需要被加載,也就是說(shuō)單元格是可以循環(huán)利用的智听。想象一下羽杰,如果你的表格視圖有 5000 個(gè)單元格并在進(jìn)入屏幕的時(shí)候全部被加載,這樣的話到推,即使你的應(yīng)用沒有因?yàn)閮?nèi)存壓力而崩潰考赛,用戶體驗(yàn)也一定會(huì)非常的糟糕。你自定義的視圖也同樣應(yīng)該遵守這一原則莉测。再舉個(gè)栗子颜骤,如果你在開發(fā)一個(gè)相冊(cè)類應(yīng)用,不要一口氣把所有圖片都加載完捣卤,你只需加載顯示在屏幕上的那些圖片就夠了忍抽。這種技術(shù)也通常被稱作延遲加載(lazy loading)。
延遲加載(lazy loading)
延遲加載技術(shù)的主旨就是盡可能晚地去加載資源董朝,這樣會(huì)帶來(lái)以下兩點(diǎn)優(yōu)勢(shì):
- 可以更好地來(lái)分配不同的加載時(shí)間
- 可以避免加載那些可能不需要的資源
那么在 iOS 開發(fā)中如何使用延遲加載技術(shù)呢鸠项?正如前文提到的,表格視圖就是一個(gè)很好的使用延遲加載技術(shù)的栗子子姜。另一種使用延遲加載的方法是使用lazy
關(guān)鍵字來(lái)修飾屬性祟绊。想象一下你需要一個(gè)包含所有產(chǎn)品的數(shù)組,當(dāng)用戶進(jìn)行一定交互時(shí)需要使用到它們。
var products: [Products] = modelClass.loadProducts()
如上代碼牧抽,這個(gè)數(shù)組即使在用戶沒有進(jìn)行任何交互的情況下仍然會(huì)被加載嘉熊,這是一種內(nèi)存浪費(fèi)。如果加上lazy
關(guān)鍵字進(jìn)行修飾阎姥,那么只有在用戶第一次訪問數(shù)組的時(shí)候它才會(huì)初始化记舆。
lazy var products: [Products] = modelClass.loadProducts()
即使只是一些小的數(shù)組和變量,合理地使用延遲加載技術(shù)也能節(jié)省很大一部分內(nèi)存呼巴。
視圖控制器和循環(huán)引用
在所有內(nèi)存問題中最壞的一種情況就是視圖控制器不再需要的時(shí)候卻沒有被釋放,出現(xiàn)這種情況最通常的原因是循環(huán)引用御蒲。試想一下衣赶,現(xiàn)在有一個(gè)控制器 A,它擁有一個(gè)控制器 B 作為它的子控制器厚满,而且府瞄,控制器 B 還有一個(gè)指向控制器 A 的引用,這樣它們都互相強(qiáng)引用著對(duì)方碘箍。
現(xiàn)在遵馆,即使控制器 A 從屏幕中離開,兩個(gè)控制器也不會(huì)被釋放丰榴,因?yàn)樗鼈冞€都強(qiáng)引用著對(duì)方货邓。要避免這種情況你可以使用weak
關(guān)鍵字。舉個(gè)栗子四濒,想要將控制器 A 設(shè)置為控制器 B 的代理换况,正確的屬性聲明應(yīng)該如下所示:
weak var delegate: DelegateType?
如果想檢查控制器是否被正確釋放,可以在控制器的deinit
方法中打印消息來(lái)查看盗蟆,代碼如下:
deinit {
println("deinit")
}
接下來(lái)你就可以通過(guò)在控制臺(tái)中觀察戈二,是否有輸出來(lái)檢查控制器對(duì)象是否被正確釋放。比如說(shuō)喳资,當(dāng)你的控制器是被導(dǎo)航控制器通過(guò)push
方法展現(xiàn)出來(lái)的時(shí)候觉吭,如果你點(diǎn)擊了導(dǎo)航條上的返回按鈕,控制器應(yīng)該被釋放并且在控制臺(tái)中輸出信息仆邓。
監(jiān)測(cè)你的內(nèi)存使用量
我們通常是在項(xiàng)目開發(fā)的最后階段才發(fā)現(xiàn)內(nèi)存管理的很糟糕鲜滩,不幸的是,這樣已經(jīng)太晚了宏赘。所以在項(xiàng)目開發(fā)過(guò)程中绒北,經(jīng)常對(duì)內(nèi)存使用量進(jìn)行監(jiān)測(cè)是非常重要的。你只需在一臺(tái)真機(jī)上運(yùn)行你的應(yīng)用察署,然后點(diǎn)擊Xcode中調(diào)試選項(xiàng)卡下的Memory
闷游。

總結(jié)
內(nèi)存管理在移動(dòng)開發(fā)領(lǐng)域是一個(gè)非常重要的話題。如果你使用了過(guò)多的內(nèi)存消耗,應(yīng)用就會(huì)變慢甚至可能崩潰脐往。相反休吠,如果你認(rèn)真對(duì)待內(nèi)存管理問題,你就會(huì)構(gòu)建出內(nèi)存高效的應(yīng)用业簿。