內(nèi)存管理是iOS開發(fā)中的重點(diǎn)和難點(diǎn)槐臀,也是技能進(jìn)階的重要關(guān)口葡幸,也常常在面試問題中出現(xiàn)了袁。內(nèi)存管理涉及到的核心話題包括:內(nèi)存的管理規(guī)則、垃圾回收機(jī)制湿颅、內(nèi)存泄漏和循環(huán)引用等等载绿。下面整理了iOS中涉及到的內(nèi)存管理相關(guān)知識(shí)。
(一)iOS 開發(fā)中的內(nèi)存管理規(guī)則
引用計(jì)數(shù)
OC 中通過引用計(jì)數(shù)來實(shí)現(xiàn)內(nèi)存管理:當(dāng)一個(gè)對象被持有的時(shí)候計(jì)數(shù)加一油航,不再被持有的時(shí)候引用計(jì)數(shù)減一卢鹦,當(dāng)引用計(jì)數(shù)為零的時(shí)候,則將這個(gè)對象釋放劝堪。
- MRC:手動(dòng)引用計(jì)數(shù),iOS 5 之前的版本采用揉稚。
- ARC:自動(dòng)引用計(jì)數(shù)秒啦,iOS 5 以及之后的版本采用。
MRC 和 ARC 之間的轉(zhuǎn)換
- MRC 轉(zhuǎn) ARC :在控制開關(guān)中添加-fobjc-arc 以ARC進(jìn)行編譯搀玖。
- ARC 轉(zhuǎn) MRC :在控制開關(guān)中添加-fno-objc-arc 以MRC進(jìn)行編譯余境。
自動(dòng)釋放池
OC對象的生命周期取決于引用計(jì)數(shù),我們有兩種方式可以釋放對象:一種是直接調(diào)用release釋放灌诅;另一種是調(diào)用autorelease將對象加入自動(dòng)釋放池中芳来。自動(dòng)釋放池用于存放那些需要在稍后某個(gè)時(shí)刻釋放的對象。
自動(dòng)釋放池中 release 和 drain 的區(qū)別
release 和 drain 的作用是一樣的猜拾,都是清理自動(dòng)釋放池即舌,區(qū)別是:drain 在支持 GC 的系統(tǒng)中(Mac)可以引起 GC 回收操作,而 release 不可以
(二)Objective-C 開發(fā)中的垃圾回收
垃圾回收(Garbage Collection, GC)簡單地說就是程序中及時(shí)處理廢棄不用的內(nèi)存對象的機(jī)制挎袜,防止內(nèi)存中廢棄對象堆積過多造成內(nèi)存泄漏顽聂。
平臺(tái)局限性
Objective-C 語言本身是支持垃圾回收機(jī)制的,但是具有平臺(tái)局限性盯仪,僅限于Mac 桌面系統(tǒng)開發(fā)中紊搪,在iPhone 和 iPad 等蘋果移動(dòng)端開發(fā)中是不支持垃圾回收機(jī)制的。
垃圾回收和引用計(jì)數(shù)的區(qū)別
垃圾回收是宏觀的全景,對整體進(jìn)行內(nèi)存管理:將所有對象看作一個(gè)集合耀石,然后在GC循環(huán)中定時(shí)監(jiān)測活動(dòng)對象和非活動(dòng)對象,及時(shí)將永不倒的非活動(dòng)對象釋放以避免內(nèi)存泄漏爸黄,也就是說將用不到的垃圾對象交給 GC 來管理釋放滞伟,而無需開發(fā)者關(guān)心。
相比于 GC馆纳,引用計(jì)數(shù)是局部性的诗良,是管理每個(gè)對象的引用計(jì)數(shù),單個(gè)對象引用計(jì)數(shù)為0后會(huì)馬上被釋放鲁驶。
(三)僵尸對象
產(chǎn)生原因
一個(gè)引用計(jì)數(shù)為0的Objective-C被釋放后就變成僵尸對象鉴裹,也就是過度釋放的對象。
調(diào)試方法
遇到EXC_BAD_ACCESS 這類問題一般都是僵尸對象引起的,可以開啟僵尸模式(Zombie Objects)定位径荔。具體步驟這里不詳細(xì)給出督禽。
(四)野指針
產(chǎn)生原因
野指針又叫“懸掛指針”,野指針出現(xiàn)的原因是指針沒有賦值总处,或者是指針指向的對象已經(jīng)被釋放掉了狈惫。開發(fā)中應(yīng)該給野指針及時(shí)賦予零值,避免內(nèi)存報(bào)錯(cuò)鹦马。
與空指針區(qū)別:向空指針發(fā)送消息不會(huì)報(bào)錯(cuò)胧谈。
調(diào)試方法
野指針指向一塊隨機(jī)的垃圾內(nèi)存,向它們發(fā)送消息會(huì)報(bào) EXC_BAD_ACCESS 錯(cuò)誤導(dǎo)致程序崩潰荸频×庑ぃ可以開啟僵尸模式(Zombie Objects)定位。具體步驟這里不詳細(xì)給出旭从。
(五)空指針
定義
空指針不用于野指針稳强,它是一個(gè)沒有指向任何內(nèi)容的指針『驮茫空指針是有效的指針退疫,值為 nil、NULL鸽素、Nil 或者0等褒繁,給空指針發(fā)送信息不會(huì)報(bào)錯(cuò),只是不會(huì)相應(yīng)信息而已馍忽。
(六)nil澜汤、Nil、NULL 和 [NSNull null] 的區(qū)別
- nil:當(dāng)一個(gè)對象置為nil時(shí)舵匾,這個(gè)對象的內(nèi)存地址就會(huì)被系統(tǒng)收回俊抵。置空之后是不能進(jìn)行retain,copy等跟引用計(jì)數(shù)有關(guān)的任何操作的坐梯。
- Nil:nil完全等同于Nil徽诲,只不過由于編程習(xí)慣,人們一般把對象置空用nil吵血,把類置空用Nil谎替。
- NULL :這個(gè)是從C語言繼承來的,就是一個(gè)簡單的空指針蹋辅。
- [NSNull null]:[NSNull null]和nil的區(qū)別在于钱贯,nil是一個(gè)空對象,已經(jīng)完全從內(nèi)存中消失了侦另,而如果我們想表達(dá)“我們需要有這樣一個(gè)容器秩命,但這個(gè)容器里什么也沒有”的觀念時(shí)尉共,我們就用到[NSNull null],它就是為“值為空的對象”弃锐。如果你查閱開發(fā)文檔你會(huì)發(fā)現(xiàn)NSNull這個(gè)類是繼承NSObject袄友,并且只有一個(gè)“+ (NSNull *) null;”類方法霹菊。這就說明NSNull對象擁有一個(gè)有效的內(nèi)存地址剧蚣,所以在程序中對它的任何引用都是不會(huì)導(dǎo)致程序崩潰的。
(七)內(nèi)存泄漏
產(chǎn)生原因
內(nèi)存泄漏指動(dòng)態(tài)分配內(nèi)存的對象在使用完后沒有被系統(tǒng)回收旋廷,導(dǎo)致對象始終占用內(nèi)存鸠按,又無法通過代碼訪問。大量內(nèi)存泄漏會(huì)導(dǎo)致系統(tǒng)內(nèi)存不足的問題饶碘。
解決與調(diào)試方法
- 對程序員而言待诅,要深入理解內(nèi)存管理原則,養(yǎng)成良好的編程習(xí)慣以減少內(nèi)存泄漏情況的發(fā)生熊镣。
- 使用 Xcode 提供的檢測調(diào)試工具 Instruments,檢測可能導(dǎo)致內(nèi)存泄漏的代碼募书,并及時(shí)優(yōu)化绪囱。
(八)循環(huán)引用
產(chǎn)生原因
當(dāng)多個(gè)對象相互持有形成一個(gè)封閉的環(huán)時(shí),循環(huán)引用問題隨之出現(xiàn)莹捡,導(dǎo)致內(nèi)存泄漏鬼吵。
解決方法
- 自己明確知道這里會(huì)存在循環(huán)引用,在合理的位置主動(dòng)斷開環(huán)中的一個(gè)引用(置為nil)篮赢,使得對象得以回收齿椅;
- 使用弱引用。
iOS 開發(fā)中常出現(xiàn)循環(huán)引用的地方
- 代理 delegate启泣。詳情可參考 (二)iOS 開發(fā)設(shè)計(jì)模式:代理涣脚、觀察者、單例和工廠模式 中代理部分寥茫。
- block 容易導(dǎo)致循環(huán)引用遣蚀。
- NSTimer 引起的循環(huán)引用。具體詳情查看鏈接 iOS實(shí)錄8:解決NSTimer/CADisplayLink的循環(huán)引用
(九)深拷貝和淺拷貝
淺拷貝只是復(fù)制了內(nèi)存地址纱耻,也就是對內(nèi)存空間的引用芭梯;深拷貝是開辟新的空間并且復(fù)制原空間相同的內(nèi)容,新指針指向新空間內(nèi)容弄喘。
對 immutable 對象進(jìn)行 copy 操作玖喘,是淺拷貝,mutableCopy 操作是深拷貝蘑志;對 mutable 對象進(jìn)行 copy 和 mutableCopy 都是深拷貝累奈。