手動內存管理的機理你們應該已經很是清楚了,簡單來講,只要遵循如下三點就能夠在手動內存管理中避免絕大部分的麻煩:objective-c
若是須要持有一個對象,那麼對其發送retain 若是以後再也不使用該對象,那麼須要對其發送release(或者autorealse) 每一次對retain,alloc或者new的調用,須要對應一次release或autorealse調用設計模式
初學者可能僅僅只是知道這些規則,可是在實際使用時不免犯錯。可是當開發者常用手動引用計數 Manual Referecen Counting(MRC)的話,這些規則將逐漸變爲本能。你會發現少一個release
的代碼怎麼看怎麼彆扭,從而減小或者杜絕內存管理的錯誤。能夠說MRC的規則很是簡單,可是同時也很是容易出錯。每每很小的錯誤就將引發crash或者OOM之類的嚴重問題。api
在MRC的年代裏,爲了不不當心忘寫release
,Xcode提供了一個很實用的小工具來幫助可能存在的代碼問題(Xcode3裏默認快捷鍵Shift+A?不記得了),能夠指出潛在的內存泄露或者過多釋放。而ARC在此基礎上更進一步:ARC是Objective-C編譯器的特性,而不是運行時特性或者垃圾回收機制,ARC所作的只不過是在代碼編譯時爲你自動在合適的位置插入release
或autorelease
,就如同以前MRC時你所作的那樣。所以,至少在效率上ARC機制是不會比MRC弱的,而由於能夠在最合適的地方完成引用計數的維護,以及部分優化,使用ARC甚至能比MRC取得更高的運行效率。app
學習ARC很簡單,在MRC時代你須要本身retain
一個想要保持的對象,而如今不須要了。如今惟一要作的是用一個指針指向這個對象,只要指針沒有被置空,對象就會一直保持在堆上。當將指針指向新值時,原來的對象會被release
一次。這對實例變量,synthesize的變量或者局部變量都是適用的。好比函數
NSString *firstName = self.textField.text;
firstName
如今指向NSString對象,這時這個對象(textField
的內容字符串)將被hold住。好比用字符串@「OneV"做爲例子(雖然實際上不該該用字符串舉例子,由於字符串的retainCount規則其實和普通的對象不同,你們就把它看成一個普通的對象來看吧…),這個時候firstName
持有了@"OneV"。工具
固然,一個對象能夠擁有不止一個的持有者(這個相似MRC中的retainCount>1的狀況)。在這個例子中顯然self.textField.text
也是@「OneV",那麼如今有兩個指針指向對象@"OneV」(被持有兩次,retainCount=2,其實對NSString對象說retainCount是有問題的,不過anyway~就這個意思而已.)。學習
過了一下子,也許用戶在textField
裏輸入了其餘的東西,那麼self.textField.text
指針顯然如今指向了別的字符串,好比@「onevcat",可是這時候原來的對象已然是存在的,由於還有一個指針firstName
持有它。如今指針的指向關係是這樣的:優化
只有當firstName
也被設定了新的值,或者是超出了做用範圍的空間(好比它是局部變量可是這個方法執行完了或者它是實例變量可是這個實例被銷燬了),那麼此時firstName
也再也不持有@「OneV",此時再也不有指針指向@"OneV",在ARC下這種情況發生後對象@"OneV"即被銷燬,內存釋放。atom
相似於firstName
和self.textField.text
這樣的指針使用關鍵字strong
進行標誌,它意味着只要該指針指向某個對象,那麼這個對象就不會被銷燬。反過來講,ARC的一個基本規則便是,只要某個對象被任一strong
指針指向,那麼它將不會被銷燬。若是對象沒有被任何strong指針指向,那麼就將被銷燬。在默認狀況下,全部的實例變量和局部變量都是strong
類型的。能夠說strong
類型的指針在行爲上和MRC時代retain
的property是比較類似的。設計
既然有strong
,那確定有weak
咯~weak
類型的指針也能夠指向對象,可是並不會持有該對象。好比:
__weak NSString *weakName = self.textField.text
獲得的指向關係是:
這裏聲明瞭一個weak
的指針weakName
,它並不持有@「onevcat"。若是self.textField.text
的內容發生改變的話,根據以前提到的"只要某個對象被任一strong指針指向,那麼它將不會被銷燬。若是對象沒有被任何strong指針指向,那麼就將被銷燬」原則,此時指向@「onevcat"的指針中沒有strong
類型的指針,@"onevcat"將被銷燬。同時,在ARC機制做用下,全部指向這個對象的weak
指針將被置爲nil
。這個特性至關有用,相信無數的開發者都曾經被指針指向已釋放對象所形成的EXCBADACCESS困擾過,使用ARC之後,不管是strong
仍是weak
類型的指針,都再也不會指向一個dealloced的對象,從根源上解決了意外釋放致使的crash。
不過在大部分狀況下,weak
類型的指針可能並不會很經常使用。比較常見的用法是在兩個對象間存在包含關係時:對象1有一個strong
指針指向對象2,並持有它,而對象2中只有一個weak
指針指回對象1,從而避免了循環持有。一個常見的例子就是oc中常見的delegate設計模式,viewController中有一個strong
指針指向它所負責管理的UITableView,而UITableView中的dataSource
和delegate
指針都是指向viewController的weak
指針。能夠說,weak
指針的行爲和MRC時代的assign
有一些類似點,可是考慮到weak
指針更聰明些(會自動指向nil),所以仍是有所不一樣的。細節的東西咱們稍後再說。
注意相似下面的代碼彷佛是沒有什麼意義的:
__weak NSString *str = [[NSString alloc] initWithFormat:…]; NSLog(@"%@",str); //輸出是"(null)"
因爲str
是weak
,它不會持有alloc出來的NSString
對象,所以這個對象因爲沒有有效的strong
指針指向,因此在生成的同時就被銷燬了。若是咱們在Xcode中寫了上面的代碼,咱們應該會獲得一個警告,由於不管什麼時候這種狀況彷佛都是不太可能出現的。你能夠把weak換成strong來消除警告,或者直接前面什麼都不寫,由於ARC中默認的指針類型就是strong
。
property也能夠用strong
或weak
來標記,簡單地把原來寫retain
和assign
的地方替換成strong
或者weak
就能夠了。
@property (nonatomic, strong) NSString *firstName; @property (nonatomic, weak) id delegate;
ARC能夠爲開發者節省不少代碼,使用ARC之後不再須要關心何時retain
,何時release
,可是這並不意味你能夠不思考內存管理,你可能須要常常性地問本身這個問題:誰持有這個對象?
好比下面的代碼,假設array
是一個NSMutableArray
而且裏面至少有一個對象:
id obj = [array objectAtIndex:0]; [array removeObjectAtIndex:0]; NSLog(@"%@",obj);
在MRC時代這幾行代碼應該就掛掉了,由於array
中0號對象被remove之後就被當即銷燬了,所以obj指向了一個dealloced的對象,所以在NSLog的時候將出現EXCBADACCESS。而在ARC中因爲obj是strong
的,所以它持有了array
中的首個對象,array
再也不是該對象的惟一持有者。即便咱們從array
中將obj移除了,它也依然被別的指針持有,所以不會被銷燬。
ARC也有一些缺點,對於初學者來講,可能僅只能將ARC用在objective-c對象上(也即繼承自NSObject的對象),可是若是涉及到較爲底層的東西,好比Core Foundation中的malloc()或者free()等,ARC就鞭長莫及了,這時候仍是須要本身手動進行內存管理。在以後咱們會看到一些這方面的例子。另外爲了確保ARC能正確的工做,有些語法規則也會由於ARC而變得稍微嚴格一些。
ARC確實能夠在適當的地方爲代碼添加retain
或者release
,可是這並不意味着你能夠徹底忘記內存管理,由於你必須在合適的地方把strong
指針手動設置到nil,不然app極可能會oom。簡單說仍是那句話,你必須時刻清醒誰持有了哪些對象,而這些持有者在何時應該變爲指向nil
。
ARC必然是Objective-C以及Apple開發的趨勢,從此也會有愈來愈多的項目採用ARC(甚至不排除MRC在將來某個版本被棄用的可能),Apple也一直鼓勵開發者開始使用ARC,由於它確實能夠簡化代碼並加強其穩定性。能夠這麼說,使用ARC以後,因爲內存問題形成的crash基本就是過去式了(OOM除外 :P)
咱們正處於由MRC向ARC轉變的節點上,所以可能有時候咱們須要在ARC和MRC的代碼間來回切換和適配。Apple也想到了這一點,所以爲開發這提供了一些ARC和非ARC代碼混編的機制,這些也將在以後的例子中列出。另外ARC甚至能夠用在C++的代碼中,而經過遵照一些代碼規則,iOS 4裏也可使用ARC(雖然我我的認爲在如今iOS 6都呼之欲出的年代已經基本沒有須要爲iOS 4作適配的必要了)、
總之,聰明的開發者總會嘗試儘量的自動化流程,已減輕本身的工做負擔,而ARC偏偏就爲咱們提供了這樣的好處:自動幫咱們完成了不少之前須要手動完成的工做,所以對我來講,轉向ARC是一件不須要考慮的事情。
ARC中關於對象的引用參照,主要有下面幾關鍵字。使用strong, weak, autoreleasing限定的變量會被隱式初始化爲nil。
變量聲明缺省都帶有__strong關鍵字,若是變量什麼關鍵字都不寫,那麼缺省就是強參照。
上面已經看到了,這是弱參照的關鍵字。該概念是新特性,從 iOS 5/ Mac OS X 10.7 開始導入。因爲該類型不影響對象的生命週期,因此若是對象以前就沒有持有者,那麼會出現剛建立就被破棄的問題,好比下面的代碼。
若是編譯設定OS版本 Deployment Target 設定爲這比這低的版本,那麼編譯時將報錯(The current deployment target does not support automated __weak references),這個時候,咱們可使用下面的 __unsafe_unretained。
弱參照還有一個特徵,即當參數對象失去全部者以後,變量會被自動付上nil (Zeroing)。
該關鍵字與__weak同樣,也是弱參照,與__weak的區別只是是否執行nil賦值(Zeroing)。可是這樣,須要注意變量所指的對象已經被破棄了,地址還還存在,但內存中對象已經沒有了。若是仍是訪問該對象,將引發「BAD_ACCESS」錯誤。
該關鍵字使對像延遲釋放。好比你想傳一個未初始化的對像引用到一個方法當中,在此方法中實例化此對像,那麼這種狀況可使用__autoreleasing。他被常常用於函數有值參數返回時的處理,好比下面的例子。
又如函數的返回值是在函數中申請的,那麼但願釋放是在調用端時,每每有下面的代碼。
即當方法的參數是id*,且但願方法返回時對象被autoreleased,那麼使用該關鍵字。
補充一個 循環應用問題
由於循環引用而產生的內存泄露也是Instrument沒法發現的,因此要特別當心。