? ? ? 最近在拉鉤看到一個(gè)面試評論,有個(gè)哥們在優(yōu)酷面完后評論--“ARC下有誰會用autorelease,問我autorelease后的對象何時(shí)釋放,老子能知道?要是在流媒體開發(fā)中會用到它我腦袋削下來給你當(dāng)板凳?!?/p>
? ? ? 看完評論的我也陷入沉思,這似乎優(yōu)點(diǎn)像以前的我---總在找別人的毛病,曾經(jīng)的我一直覺得iOS開發(fā)哪需要什么算法,整天在寫UI,頂多寫個(gè)冒泡排序,面試算法多此一舉。不過經(jīng)過多輪的面試,心境早已變了,開始嘗試著站在對方角度看問題,畢竟抱怨對自己沒有一點(diǎn)助益,現(xiàn)在的我都開始啃《算法導(dǎo)論》《代碼大全》。對那個(gè)兄弟,我只想說----停止抱怨,你會得到更多。作為一個(gè)iOS程序員(快滿大街的職業(yè)),說自己會自動(dòng)布局,會寫tableview代理數(shù)據(jù)源,...,怎么能凸顯出你的專業(yè)素養(yǎng)與逼格?我們需要打造自己的技術(shù)護(hù)城河,最近研究OpenGL ES也有些心得,過段時(shí)間可能寫圖像處理方面的blog.
? ? ? 閑話不說了,步入正題,本文主要圍繞MRC下內(nèi)存管理概念及重要的注意點(diǎn)(這對于理解ARC內(nèi)存管理也很有幫助,而且面試經(jīng)常遇到)
本文中大部分內(nèi)容來自Advanced Memory Management Programming Guide,有興趣的可以自行參考文檔,我也會在適當(dāng)?shù)胤浇o出原文。
一.MRC下的內(nèi)存管理(先簡單回顧,重點(diǎn)在后面)
無論在MRC還是ARC下都是采用引用計(jì)數(shù)來管理內(nèi)存,只不過在ARC下編譯器在編譯時(shí)為你自動(dòng)插入了retain,release,autorelease等內(nèi)存管理語句,當(dāng)對象的引用計(jì)數(shù)為0時(shí)會被釋放。
retain,release語句成對出現(xiàn),retain引用計(jì)數(shù)+1,release引用計(jì)數(shù)-1.
基本的內(nèi)存管理原則:
1.使用alloc,new, copy, mutablecopy創(chuàng)建的對象,你自動(dòng)獲得所有權(quán),即引用計(jì)數(shù)加1.
2.要獲得一個(gè)對象的所有權(quán),使用retain.
3.當(dāng)你不需要使用時(shí),釋放你擁有的對象,使用release,autorelease.
注意點(diǎn):
1.autorelease的出現(xiàn)是為了實(shí)現(xiàn)返回對象的方法。(可能是原因之一吧)
- (NSString *)name {
? ? ? ? ? NSString *str = [[NSString alloc] initWithFormat:@"hello"];
//在MRC下,若不用autorelease,無法返回str,像這樣,str將會泄漏。你必須想辦法釋放它。若直接釋放,則在返回之前str就已經(jīng)無效了。
? ? ? ? ?return str;
}
2.集合類對象自動(dòng)對存入的對象進(jìn)行retain,移除對象或者銷毀集合本身時(shí)也會解除擁有關(guān)系,發(fā)送release。
3.不要使用dealloc方法管理稀缺資源。
什么稀缺資源?原文這樣說--You should typically not manage scarce resources such as file descriptors, network connections, and buffers or caches in a dealloc method.
原因:dealloc可能會被推遲執(zhí)行,甚至被繞過不執(zhí)行。(什么時(shí)候會繞過呢,當(dāng)app被結(jié)束時(shí),系統(tǒng)不會挨個(gè)調(diào)用dealloc回收內(nèi)存,這樣比較低效,系統(tǒng)直接自己釋放回收了app占用的內(nèi)存。)
導(dǎo)致的嚴(yán)重問題:1.資源得不到正確釋放,比如文件資源,那磁盤將會被占滿,用戶無法存儲東西(If your application runs out of file descriptors, for example, the user may not be able to save data.)比內(nèi)存泄漏嚴(yán)重多了,是不是需要清理磁盤或重裝系統(tǒng)。
2.清理的邏輯在錯(cuò)誤的線程執(zhí)行。若對象在不恰當(dāng)時(shí)間autorelease,它將在不確定的某個(gè)線程的自動(dòng)釋放池中銷毀,對于有些資源要求只能在一個(gè)線程訪問來說,后果是嚴(yán)重的。(If an object is autoreleased at an unexpected time, it will be deallocated on whatever thread’s autorelease pool block it happens to be in. This can easily be fatal for resources that should only be touched from one thread.)
面試中的那些問題
1.autorelease的對象什么時(shí)候釋放?
在它所在的最內(nèi)層自動(dòng)釋放池結(jié)束時(shí),它會收到release消息釋放。每一個(gè)線程都維護(hù)著屬于自己的自動(dòng)釋放池棧。
2.什么樣的對象會被放入自動(dòng)釋放池?
收到autorelease消息的對象,在ARC下,若不是用alloc, new,copy, mutablecopy創(chuàng)建的對象,一般可以認(rèn)為系統(tǒng)自動(dòng)添加了autorelease,所以會被放入自動(dòng)釋放池,而alloc等創(chuàng)建的不會放入,在ARC下若要盡早釋放alloc等方法創(chuàng)建的對象,只需要賦值nil就好了。沒有強(qiáng)引用就會自動(dòng)釋放。自動(dòng)釋放池只對接收了autorelease消息的對象有用。
3.多線程與自動(dòng)釋放池,新創(chuàng)建的線程需要?jiǎng)?chuàng)建釋放池嗎?
若使用NSThread創(chuàng)建的線程,需要?jiǎng)?chuàng)建釋放池,否則會造成內(nèi)存泄漏。默認(rèn)只在主線程的運(yùn)行循環(huán)中自動(dòng)創(chuàng)建自動(dòng)釋放池,所以一般有關(guān)UI的操作,不需要手動(dòng)創(chuàng)建自動(dòng)釋放池,在每一個(gè)循環(huán)結(jié)束時(shí),釋放池中的對象會被釋放。(Each thread in a Cocoa application maintains its own stack of autorelease pool blocks. If you are writing a Foundation-only program or if you detach a thread, you need to create your own autorelease pool block.)
4.使用gcd 、OperationQueue等創(chuàng)建的線程需要?jiǎng)?chuàng)建釋放池嗎?
不需要,我在Concurrency Programming Guide中找到一段話,Although GCD dispatch queues have their own autorelease pools, they make no guarantees as to when those pools are drained. If your application is memory constrained, creating your own autorelease pool allows you to free up the memory for autoreleased objects at more regular intervals.
大致意思就是,gcd的隊(duì)列有自己的自動(dòng)釋放池,但是不保證什么時(shí)候釋放,如果你需要盡快釋放內(nèi)存,你可以在block中創(chuàng)建自己的自動(dòng)釋放池以便更頻繁地釋放。所以一般情況下不需要,除非你內(nèi)存很吃緊,需要極力優(yōu)化。因?yàn)镺perationQueue等高層API是基于GCD的,所以可以大膽猜想,它必然也是有自己的自動(dòng)釋放池。
5.什么時(shí)候需要使用自動(dòng)釋放池?
官方給出的三中情形:
1.If you are writing a program that is not based on a UI framework, such as a command-line tool.(若寫的程序無關(guān)UI,比如命令行程序,)
2.If you write a loop that creates many temporary objects.(在for循環(huán)中創(chuàng)建過多臨時(shí)對象,可以用來盡早釋放,避免內(nèi)存峰值過高)
You may use an autorelease pool block inside the loop to dispose of those objects before the next iteration. Using an autorelease pool block in the loop helps to reduce the maximum memory footprint of the application.
3.If you spawn a secondary thread.(你創(chuàng)建了第二個(gè)線程)
You must create your own autorelease pool block as soon as the thread begins executing; otherwise, your application will leak objects. (SeeAutorelease Pool Blocks and Threads?blog?for details.)你需要在線程開始執(zhí)行時(shí)盡早創(chuàng)建自己的自動(dòng)釋放池,否則你的應(yīng)用將會內(nèi)存泄漏。
最后:第一次寫總結(jié),難免有紕漏,若大家發(fā)現(xiàn)有問題,希望及時(shí)指正。
拋出一個(gè)問題:Don’t Use Accessor Methods in Initializer Methods and dealloc,文檔中這句有沒理解,為什么在MRC下不允許在init和dealloc中使用屬性訪問方法而是直接使用實(shí)例變量?