IOS內存管理

軟件運行時會分配和使用設備的內存資源,因此,在軟件開發的過程中,需要進行內存管理,以保證高效、快速的分配內存,并且在適當的時候釋放和回收內存資源。

一、Objective-C內存管理的對象

iOS開發中,內存中的對象主要有兩類,一類是值類型,比如int、float、struct等基本數據類型,另一類是引用類型,也就是繼承自NSObject類的所有的OC對象。前一種值類型不需要我們管理,后一種引用類型是需要我們管理內存的,一旦管理不好,就會產生非常糟糕的后果。

為什么值類型不需要管理,而引用類型需要管理呢?那是因為他們分配內存方式不一樣。值類型會被放入棧中,他們依次緊密排列,在內存中占有一塊連續的內存空間,遵循先進后出的原則。引用類型會被放到堆中,當給對象分配內存空間時,會隨機的從內存當中開辟空間,對象與對象之間可能會留有不確定大小的空白空間,因此會產生很多內存碎片,需要我們管理。

二、Objective-C管理內存的方式

Objective-C中提供了兩種內存管理機制MRC和ARC,分別提供對內存的手動和自動管理,來滿足不同的需求。

1.MRC(人工引用計數),手動管理內存。

MRC模式下,所有的對象都需要手動的添加retain、release代碼來管理內存。使用MRC,需要遵守誰創建,誰回收的原則。也就是誰alloc,誰release;誰retain,誰release。

當引用計數為0的時候,必須回收,引用計數不為0,不能回收,如果引用計數為0,但是沒有回收,會造成內存泄漏。如果引用計數為0,繼續釋放,會造成野指針。為了避免出現野指針,我們在釋放的時候,會先讓指針=nil。

2.ARC(自動引用計數),自動管理內存。

ARC是iOS5推出的新功能,通過ARC,可以自動的管理內存。在ARC模式下,只要沒有強指針(強引用)指向對象,對象就會被釋放。在ARC模式下,不允許使用retain、release、retainCount等方法。并且,如果使用dealloc方法時,不允許調用【super dealloc】方法。

ARC模式下的property變量修飾詞為strong、weak,相當于MRC模式下的retain、assign。strong代替retain,缺省關鍵詞,代表強引用。weak代替assign,聲明了一個可以自動設置nil的弱引用,但是比assign多了一個功能,指針指向的地址被釋放之后,指針本身也會自動被釋放。

三、與內存有關的修飾符

strong:強引用,ARC中使用,與MRC中retain類似,使用之后,計數器+1。

weak: 弱引用,ARC中使用,如果只想的對象被釋放了,其指向nil,可以有效的避免野指針,其引用計數為1.

readwrite:可讀可寫特性,需要生成getter方法和setter方法時使用。

readonly:只讀特性,只會生成getter方法,不會生成setter方法,不希望屬性在類外改變。

assign:賦值特性,不涉及引用計數,弱引用setter方法將傳人參數賦值給實例變量,僅設置變量時使用。

retain:表示持有特性,setter方法將傳入參數先保留,再賦值,傳入參數的retaincount會+1。

copy:表示拷貝特性,setter方法將傳入對象復制一份,需要完全一份新的變量時。

nonatomic:非原子操作,不加同步,多線程訪問可提高性能,但是線程不安全的。決定編譯器生成的setter,getter是否是原則操作。

atomic:原子操作,同步的,表示多線程安全,與nonatomic相反。

四、MRC與ARC混編

MRC與ARC理論上是不能兼容的,也就是你如果創建的項目是ARC模式的,在你的代碼中是不能使用release,否則會出現內存問題。現在大部分程序都會選擇ARC的方式,但是,很多第三方的框架是MRC模式,如果想把這些第三方的文件加到自己項目中,需要進行標識,否則編譯的時候會出現錯誤。

在ARC的項目中,對MRC的文件可以添加編譯選項-fno-objc-arc的標識;在MRC的項目中,對ARC的文件可以添加編譯選項-fobjc-arc的標識。步驟如下圖所示。


1.png

把MRC文件轉為ARC,實際上就是去掉文件中的retain、release,因此也通過下圖中方式完成。


2.png

五、關于內存管理經常會涉及的問題

1.僵尸對象、野指針、空指針分別指什么,有什么區別?

僵尸對象:一個OC對象引用計數為0被釋放后就變成僵尸對象了,僵尸對象的內存已經被系統回收,雖然可能改對象還存在,數據依然在內存中,但僵尸對象已經是不穩定對象了,不可以再訪問或者使用,它的內存是隨時可能被別的對象申請而占用的;

野指針:野指針出現的原因是指針沒有賦值,或者指針指向的對象已經被釋放掉了,野指針指向一塊隨機的垃圾內存,向他們發送消息會報EXC_BAD_ACCESS錯誤導致程序崩潰;

空指針:空指針不同于野指針,它是一個沒有指向任何東西的指針,空指針是有效指針,值為nil,NULL,或0等,給空指針發送消息不會報錯,只是不想贏消息而已,應該給野指針及時賦予零值變成有效的空指針,避免內存報錯。

2.Objectvie-C有GC垃圾回收機制嗎?

GC(Garbage Collection),垃圾回收機制,簡單地說就是程序中及時處理廢棄不用的內存對象的機制,防止內存中廢棄對象堆積過多造成內存泄漏。Objective-C語言本身是支持垃圾回收機制的,但有平臺局限性,僅限于Mac桌面系統開發中,而在iPhone和iPad等蘋果移動終端設備中是不支持垃圾回收機制的。在移動設備開發中的內存管理是采用MRC以及iOS5以后的ARC了,本質都是RC引用計數,通過引用計數的方式來管理內存的分配與釋放,從而防止內存泄漏。

另外引用計數RC和垃圾回收GC是有區別的。垃圾回收是宏觀的,對整體進行內存管理,雖然不同平臺垃圾回收機制有異,但基本原理都是一樣的:將所有對象看做一個集合,然后在GC循環中定時檢測活動對象和非活動對象,及時將用不到的非活動對象釋放掉來避免內存泄漏,也就是說用不到的垃圾對象是交給GC來管理釋放的,而無需開發者關心,典型的是Java中的垃圾回收機制;相比于GC,引用計數是局部性的的,開發者要管理控制每個對象的引用計數,打個對象引用計數巍峨0后會馬上被釋放掉。ARC自動引用計數則是一種改進,由編譯器幫助開發者自動管理控制引用計數(自動在核實的時機發送release和retain消息)。另外自動釋放池autorelease pool則像是一個局部的垃圾回收,將部分垃圾對象幾種釋放,相對于單個釋放會有一定延遲。

相關問題:自動釋放池跟GC(垃圾回收)有什么區別?iPhone上有GC嗎?[pool release]和[pool drain]有什么區別?

[pool release]和[pool drain]在作用上是一樣的,都是傾倒自動釋放池,區別是drain在支持GC垃圾回收的系統中(Mac系統)可以引起GC回收操作,而release不可以。對于自動釋放池一般還是使用[pool drain]了,一是它的功能對系統兼容性更強,二者也是為了跟普通對象的release釋放區別開。了自動釋放池的釋放操作指的是向池內所有的對象發送release消息,以讓系統及時釋放池內的所有對象。

3.如果一個對象釋放前被加到了NotificationCenter中,不在NotificationCenter中remove這個對象可能回出現什么問題?

首先對于NotificationCenter的使用,我們都知道,只要添加對象到消息中心進行通知注冊,之后就一定要對其remove進行通知注銷。將對象添加到消息中心后,消息中心只是保存該對象的地址,消息中心到時候回根據地址發送通知給該對象,但并沒有取得該對象的強引用,對象的引用計數不會加1。如果對象釋放后卻沒有從消息中心remove掉進行通知注銷,也就是通知中心還保存著那個指針,而那個指針的對象可能已經被釋放銷毀了,那個指針就成為一個野指針,當通知發生時,會向這個野指針發送消息導致程序崩潰。

4.Objective-C是如何實現內存管理的? autorelease pool自動釋放池是什么? autorelease的對象是在什么時候被release的? autorelease和release有什么區別?

引用計數

Objective-C的內存管理本質上是通過引用計數實現的,每次Runloop都會檢查對象的引用計數,如果引用計數為0,說明該對象已經沒人用了,可以對其進行釋放了。其中引用計數可以大體分三種:MRC(手動內存計數),ARC(自動內存計數,iOS5以后)和內存池。

其中引用計數是如何操作的呢?不論哪種引用計數方式,本質都是在合適的時機將對象的引用計數加1或者減一。

這里簡單歸結一下:

使對象引用計數加1的常見操作有:alloc,copy,retain

使對象引用計數減1的常見操作有:release,autorelease

自動釋放池

自動釋放池是一個統一來釋放一組對象的容器,在向對象發送autorelease消息時,對象并沒有立即釋放,而是將對象加入到最新的自動釋放池(即將該對象的引用交給自動釋放池,之后統一調用release),自動釋放池會在程序執行到作用域結束的位置時進行drain釋放操作,這個時候會對池中的每一個對象都發送release消息來釋放所有對象。這樣其實就實現了這些對象的延遲釋放。

自動釋放吃釋放的時機,也就是自動釋放池內的所有對象是在什么時候釋放的,這里要提到程序的運行周期RunLoop。對于每一個新的RunLoop,系統都會隱式的創建一個autorelease pool,RunLoop結束時自動釋放池變回進行對象釋放操作。

autorelease和release的區別主要是引用計數減一的時機不同,autorelease會在對象的使用真正結束的時候才做引用計數減1,而不是收到消息立馬釋放。

retain,release和autorelease的底層實現

最后通過了解這三者的較底層實現來理解它們的本質區別:

-(id)retain{

//對象引用計數加1

NSIncrementExtraRefCount(self);

return self;

}

-(void)release{

//對象引用計數減1,之后如果引用計數為0則釋放

if(NSDecrementExtraRefCountWasZero(self)){

NSDeallocateObject(self);

}

}

-(id)autorelease{

//添加對象到自動釋放池

【NSAutoreleasePool addObject:self];

return self;

}

5.問題:為什么很多內置的類,如TableViewController的delegate的屬性是assign不是retain?

delegate代理的屬性通常設置為assign或者weak是為了避免循環引用,所有的引用計數系統,都存在循環引用的問題,但也有個別特殊情況,個別類的代理例如CAAnimation的delegate就是使用strong強引用。

其它問法:委托的property聲明用什么屬性?為什么?

6.CAAnimation的delegate代理是強引用還是弱引用?

CAAnimation的代理是強引用,是內存管理中的其中一個罕見的特例。我們知道為了避免循環引用問題,delegate代理一般都使用weak修飾表示弱引用的,而CAAnimation動畫是異步的,如果動畫的代理是弱引用不是強引用的話,會導致其隨時都可能被釋放掉。在使用動畫時要注意采取措施避免循環引用,例如及時在試圖移除之前的合適時機移除動畫。

CAAnimation的代理定義如下,明確說了動畫的代理在動畫對象整個生命周期是被強引用的,默認為nil。

7.問題:OC中,與alloc語義相反的方法是dealloc還是release?與retain語義相反的方法是dealloc還是release?需要與alloc配對使用的方式dealloc還是release,為什么?

alloc與dealloc語義相反,alloc是創建變量,dealloc是釋放變量;

retain與release語義相反,retain保留一個對象,調用后使變量的引用計數加1,而release釋放一個對象,調用后使變量的引用計數減1。

雖然alloc對應dealloc,retain對應release,但是與alloc配對使用的方法是release,而不是dealloc,為什么呢?這要從他們的實際效果來看。事實上alloc和release配對使用知識表象,本質上其實還是retain和release的配對使用。alloc用來創建對象,剛創建的對象默認引用計數為1,相當于調用alloc創建對象過程中同時會調用一次retain使對象引用計數加1,自然要有對應的release的一次調用,使對象在不用時能夠被釋放掉防止內存泄漏。

此外,dealloc是在對象引用計數為0以后系統自動調用的,dealloc沒有使對象引用計數減1的作用,只是在對象引用計數0后被系統調用進行內存回收的收尾工作。

8.內存管理的幾條原則是什么? 按照默認法則,哪些關鍵字生成的對象需要手動釋放?哪些對象不需要手動釋放會自動進入釋放池?在和property結合的時候怎樣有效的避免內存泄漏?

起初MRC中開發者要自己手動管理內存,基本原則是:誰創建,誰釋放,誰引用,誰管理。其中創建主要始于關鍵詞new,alloc和copy的使用,創建并持有開始引用計數為1,如果引用要通過發送retain消息增加引用計數,通過發送release消息減少引用計數,引用計數為0后對象會被系統清理釋放。現在有了ARC后編譯器會自動管理引用計數,開發者不再需要也不可以再手動顯示管理引用計數。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容

  • 從上圖可以看到,棧里面存放的是值類型,堆里面存放的是對象類型。對象的引用計數是在堆內存中操作的。下面我們講講堆和棧...
    jackyshan閱讀 1,663評論 2 11
  • Copyright ? 2017年ZaneWangWang. All rights reserved. 如果你看到...
    2897275c8a00閱讀 922評論 0 1
  • 自動引用計數 什么是自動引用計數內存管理/引用計數ARC規則ARC的實現 1.1 什么是自動引用計數 ARC和MR...
    凡幾多閱讀 905評論 0 5
  • 為什么管理內存: 程序在運行的時候,要創建大量的對象,這些對象放在堆和棧上。(基本類型放在棧上,由系統自動管理。)...
    我是誰重要嗎閱讀 1,401評論 0 12
  • 馮·諾依曼體系:運算器 控制器 存儲器 輸入與輸出 內存即存儲器,用來存儲指令與數據 注:哈佛體系與普林斯頓體系的...
    小李龍彪閱讀 667評論 0 8