內(nèi)存管理是指軟件運(yùn)行時(shí)對(duì)計(jì)算機(jī)內(nèi)存資源的分配和使用的技術(shù)。其最主要的目的是如何高效,快速的分配,并且在適當(dāng)?shù)臅r(shí)候釋放和回收內(nèi)存資源。
引言
面試題:iOS內(nèi)管管理方式是什么?使用過(guò)MRC嗎?
iOS中,分有兩種內(nèi)存管理方法:
- ARC :自動(dòng)引用計(jì)數(shù)
- MRC :手動(dòng)引用計(jì)數(shù)
現(xiàn)在的iOS程序中,都是使用ARC了。
自動(dòng)引用計(jì)數(shù):是指內(nèi)存管理中對(duì)引用采取自動(dòng)計(jì)數(shù)的技術(shù)
那么問(wèn)題來(lái)了:
- 哪些對(duì)象或者內(nèi)容,可以使用此技術(shù)?
- 引用是什么?計(jì)數(shù)又是什么?
- iOS如何做到自動(dòng)引用計(jì)數(shù)的?
內(nèi)存管理 - 引用計(jì)數(shù)
引用計(jì)數(shù)是什么?
引用計(jì)數(shù)是計(jì)算機(jī) 編程語(yǔ)言 中的一種** 技術(shù)** 內(nèi)存管理 ,是指將資源(可以是 對(duì)象 、 內(nèi)存 或 磁盤(pán) 空間等等)的被 引用 次數(shù)保存起來(lái),當(dāng)被引用次數(shù)變?yōu)榱銜r(shí)就將其釋放的過(guò)程。使用引用計(jì)數(shù)技術(shù)可以實(shí)現(xiàn)自動(dòng)資源管理的目的。
— — 截取自百度百科
引申一個(gè)iOS 內(nèi)存分區(qū) 概念
因?yàn)?code>OC是
C語(yǔ)言的超集,所以O(shè)C的內(nèi)存分區(qū)模式,可以說(shuō)直接用的是C語(yǔ)言的內(nèi)存分區(qū)模型
內(nèi)存分區(qū)圖
如上圖,內(nèi)存分區(qū)一共會(huì)分為5塊:
-
棧區(qū)(stack)
- 此區(qū)域存放局部變量
- 函數(shù)跳轉(zhuǎn)地址及相關(guān)參數(shù)
- 此區(qū)域是從高地址到低地址分配內(nèi)存的
- 此區(qū)域內(nèi)容不需要程序員管理
-
堆區(qū)(heap)
- 分配我們通過(guò)alloc出的對(duì)象
- 是從低地址到高地址分配內(nèi)存的
- 此區(qū)域內(nèi)容必須由程序員進(jìn)行管理
全局靜態(tài)區(qū)
此區(qū)域必須要程序員管理常量區(qū)
此區(qū)域不需要程序員管理代碼區(qū)
什么是引用計(jì)數(shù)?
通過(guò)解釋可以看出,引用計(jì)數(shù)是用于內(nèi)存管理的。解釋中,被管理的目標(biāo)是”對(duì)象”,那請(qǐng)問(wèn)對(duì)象是什么來(lái)的?
在我們的編碼過(guò)程中,需要進(jìn)行內(nèi)存管理的目標(biāo),分為兩類:
對(duì)象 與 常量
對(duì)象:即為我們通常用alloc / new出來(lái)的
對(duì)象中,又分:
是否被全局或者靜態(tài)修飾的對(duì)象
其他普通對(duì)象
常量:就是普通的內(nèi)容,基本數(shù)據(jù)類型、字符串等都有可能
對(duì)于內(nèi)存分區(qū)中的介紹中,只有在堆區(qū)的內(nèi)容,需要我們做內(nèi)存管理。而能進(jìn)入到堆區(qū)的,是對(duì)象中普通對(duì)象。
創(chuàng)建并分配堆內(nèi)存空間的對(duì)象,需要被引用并記錄的是其被引用的次數(shù)。
所以稱為”引用計(jì)數(shù)“
所以,需要使用引用計(jì)數(shù)來(lái)進(jìn)行管理的目標(biāo) 是 存放在堆區(qū)的由程序員手動(dòng)創(chuàng)建出來(lái)的
引用計(jì)數(shù)的作用
- 當(dāng)一個(gè)對(duì)象被創(chuàng)建并在堆內(nèi)分配內(nèi)存后,對(duì)象自身將會(huì)有一個(gè)屬性-引用計(jì)數(shù),此時(shí)引用計(jì)數(shù)為1
- 當(dāng)有其他對(duì)象需要持有該對(duì)象時(shí),引用計(jì)數(shù)+1
- 當(dāng)有其他對(duì)象需要釋放(放棄持有)該對(duì)象時(shí),引用計(jì)數(shù)-1
- 當(dāng)該對(duì)象的引用計(jì)數(shù)為0時(shí),系統(tǒng)將會(huì)銷毀對(duì)象并回收其內(nèi)存地址
由此可見(jiàn),系統(tǒng)通過(guò)對(duì)象的引用計(jì)數(shù),控制著對(duì)象的生命周期,從而實(shí)現(xiàn)內(nèi)存空間資源管理的目的
一個(gè)小補(bǔ)充:
本文并不打算做代碼層面的測(cè)試,雖然,我們可以通過(guò)對(duì)對(duì)象[object retainCount]的方法來(lái)打印對(duì)象的引用計(jì)數(shù)
但是,在某些測(cè)試場(chǎng)景中,打印出的引用計(jì)數(shù)會(huì)是一個(gè)無(wú)限大的數(shù),此為系統(tǒng)內(nèi)部做了處理,且ARC下不建議直接使用retainCount方法,所以本文暫不做研究。有興趣的,可以自己通過(guò)打印,看看數(shù)是多少(絕大多數(shù)情況下retainCount的數(shù)值還是正確的)~
iOS中有關(guān)引用計(jì)數(shù)的內(nèi)存管理方法
iOS內(nèi)存管理思考方式
- 自己生成的對(duì)象,自己所持有
id obj = [[NSObject alloc] init];
- 非自己生成的對(duì)象,自己也能持有
id obj = [NSMutableArray array];
- 不再需要自己持有的對(duì)象時(shí)釋放
[obj release];
或者
[obj autorelease];
- 非自己持有的對(duì)象無(wú)法釋放
了解有哪些方法,會(huì)對(duì)對(duì)象引用計(jì)數(shù)造成變更
- 生成并持有對(duì)象:alloc/new/copy/mutablecopy
如此 引用計(jì)數(shù)+1 - 持有對(duì)象 :retain
如此 引用計(jì)數(shù)+1 - 釋放對(duì)象 :release/autorelease
如此 引用計(jì)數(shù) -1 - 回收對(duì)象 :dealloc
如此 將沒(méi)有引用計(jì)數(shù)
前面引言中已經(jīng)說(shuō)了,iOS中,基于引用計(jì)數(shù)可以引申出兩種內(nèi)存管理方式:
- ARC
- MRC
MRC手動(dòng)引用計(jì)數(shù)
即對(duì)象的引用計(jì)數(shù)管理,交由程序員手動(dòng)處理,操作系統(tǒng)(iOS)不做任何干預(yù),只在對(duì)象引用計(jì)數(shù)為0時(shí),將對(duì)象在合適時(shí)機(jī)銷毀。
自行添加各種對(duì)引用計(jì)數(shù)操作的代碼,這樣,代碼量將會(huì)增加,并一不小心,就會(huì)出現(xiàn)內(nèi)存泄漏的問(wèn)題
所以,在ARC出現(xiàn)后,基本上已經(jīng)放棄了MRC的操作了,只要一些可能的老舊第三方庫(kù)還在使用吧。(這個(gè),也應(yīng)該基本沒(méi)有了,當(dāng)然不排除還有些在用MRC的)
ARC自動(dòng)引用計(jì)數(shù)
本質(zhì)部分在引用計(jì)數(shù)式內(nèi)存管理層面,ARC并沒(méi)有改變,也是使用引用計(jì)數(shù)來(lái)管理對(duì)象。只是ARC自動(dòng)的幫助我們處理”引用計(jì)數(shù)“相關(guān)部分
ARC獨(dú)有內(nèi)存管理思考方式
首先聲明一點(diǎn):iOS內(nèi)存管理思考方式思考方式就是因?yàn)锳RC的引用所帶來(lái)的變化而且思考出的一個(gè)結(jié)論,此結(jié)論對(duì)ARC和MRC均有效,但ARC多了一個(gè)所有權(quán)聲明
ARC — 所有權(quán)修飾符
簡(jiǎn)單的說(shuō),就是ARC下,使用所有權(quán)修飾符,告訴編譯器對(duì)此對(duì)象做何種內(nèi)存相關(guān)操作。
所有權(quán)修飾符有4鐘:
?__strong__weak__unsafe_unretained__autoreleasing
__strong
是所有id類型和對(duì)象的默認(rèn)所有權(quán)修飾符
作用:強(qiáng)持有此對(duì)象,在對(duì)象超出其作用域時(shí),強(qiáng)引用失效,釋放此對(duì)象
代碼演示:
--- ARC ---
{
id obj = [NSObject new];
}
--- MRC ---
{
id obj = [NSObject new];
[obj release];
}
上面兩段代碼等價(jià)!即出了作用域了,將obj釋放
__strong不僅可以作用在局部變量上,也可以作用在成員變量、屬性上。作用一致,只是需要釋放的位置,可能不同。成員變量和屬性,需要在對(duì)象被釋放前釋放掉
__weak
__strong所有權(quán),將對(duì)象強(qiáng)持有,如果釋放不當(dāng)或者使用不當(dāng),將會(huì)造成APP一個(gè)名場(chǎng)面 :****循環(huán)引用****
所以__weak的作用:提供弱引用,不能持有對(duì)象實(shí)例,即不會(huì)造成對(duì)象引用計(jì)數(shù)的改變
因?yàn)槭褂昧?code>__weak不持有對(duì)象,當(dāng)對(duì)象超出作用域后,會(huì)被立即釋放
循環(huán)引用是因?yàn)橛袃蓚€(gè)對(duì)象相互強(qiáng)引用導(dǎo)致,用__weak打破兩者間相互強(qiáng)引用的關(guān)系,即可解決循環(huán)引用的問(wèn)題。
__unsafe_unretained和__autoreleasing
一個(gè)是不安全不引用修飾符,一個(gè)是自動(dòng)釋放修飾符
前者因?yàn)槠涫褂脮?huì)造成程序出現(xiàn)不確定的異常,所以基本不用
后者因?yàn)樵贏RC下,無(wú)法使用autorelease方法,所以也是基本不使用
所有權(quán)修飾符和屬性的修飾符對(duì)應(yīng)關(guān)系如下所示:
- assign 對(duì)應(yīng)的所有權(quán)類型是 __unsafe_unretained
- copy 對(duì)應(yīng)的所有權(quán)類型是 __strong
- retain 對(duì)應(yīng)的所有權(quán)類型是 __strong
- strong 對(duì)應(yīng)的所有權(quán)類型是 __strong
- unsafe_unretained對(duì)應(yīng)的所有權(quán)類型是__unsafe_unretained
- weak 對(duì)應(yīng)的所有權(quán)類型是 __weak
ARC有效使用規(guī)則
- 不能使用 retain/release/retainCount/autorelease
- 不能使用NSAllocateObject/NSDeallocateObject
- 必須遵守內(nèi)存管理的方法命名規(guī)則
- 不要顯式調(diào)用dealloc
- 使用@autoreleasepool塊代替NSAutoreleasePool
- 不能使用區(qū)域
- 對(duì)象型變量不能作為C語(yǔ)言結(jié)構(gòu)體的成員
- 顯式轉(zhuǎn)換id和void *
ARC不適用場(chǎng)景 —— Core Foundation對(duì)象
ARC對(duì)C語(yǔ)言編寫(xiě)的Core Foundation框架中對(duì)象無(wú)效,所以,使用了該框架中的對(duì)象,需要使用其對(duì)應(yīng)的引用計(jì)數(shù)方法CFRetain/CFRealease
Foundation框架與Core Foundation框架對(duì)象的區(qū)別,只是在哪個(gè)框架下生成而已,生成之后,可以在不同框架下使用。
4C717DC7-8321-48E6-B84D-A0B70C9313A2.png

