Objective-C 高級編程讀書筆記之內(nèi)存管理

前言


項(xiàng)目中經(jīng)常會有些crash說是使用了野指針或者是內(nèi)存泄露悟衩,深覺自己的姿勢水平不夠,又翻了翻iOS界的經(jīng)典書籍《Objective-C 高級編程》籍救。將重讀過程中的一些想法體會記錄下來谦秧,供自己和大家加深理解伯复。
盡管現(xiàn)在ARC已經(jīng)做的很出色了鼠次,我們的代碼幾乎不用寫任何和內(nèi)存管理相關(guān)的代碼更哄,app可以很好的運(yùn)行不會出現(xiàn)內(nèi)存泄露。但是有沒有想過為什么可以做到這樣腥寇?畢竟c程序猿在malloc(c++里是new)完之后還要手動free(c++ 是delete)成翩,java碼農(nóng)還會時(shí)不時(shí)提一個(gè)詞GC(garbage collection,簡單說下java里的內(nèi)存管理赦役,java虛擬機(jī)會時(shí)刻監(jiān)控對象所處的狀態(tài)麻敌,根據(jù)使用情況將對象在不同年代的區(qū)域來回倒,當(dāng)處于可回收的對象達(dá)到一定程度后掂摔,就來一次集中處理术羔,這個(gè)時(shí)候整個(gè)程序會暫停(很短暫))呢赢赊,我們iOSer有沒有想過這個(gè)在OC里內(nèi)存管理是如何做的,我們知道OC是基于引用計(jì)數(shù)來做對內(nèi)存管理的级历,那這個(gè)具體又是怎么做的呢释移?即使現(xiàn)在熟悉MRC這個(gè)詞的人越來越少,但是面試官一般還是會和你聊OC內(nèi)存管理或者ARC背后的實(shí)現(xiàn)原理等鱼喉,其實(shí)還是有必要一些基本概念的秀鞭。鑒于本人的姿勢水平有些內(nèi)容可能理解有誤歡迎指出并討論趋观。


慣例先說一些基礎(chǔ)姿勢

  1. 變量作用域
    學(xué)過C語言都知道扛禽,變量作用域明確了變量的有效空間,如果出了變量的作用域就表示該變量無效了
  2. 通過引用計(jì)數(shù)來管理對象生命周期
    iOS中是通過CocoaTouch框架和運(yùn)行時(shí)來提供具體的實(shí)現(xiàn)和支持皱坛。在蘋果的實(shí)現(xiàn)方案中通過集中管理的所有對象的引用計(jì)數(shù)编曼,使用一個(gè)全局hash表存儲了對象和它引用計(jì)數(shù)的映射關(guān)系。而本書中是通過在每個(gè)對象頭部加入一個(gè)引用計(jì)數(shù)的字段來表示存儲對象引用計(jì)數(shù)的值剩辟。
    3.弱引用表來實(shí)現(xiàn)弱引用管理
    蘋果運(yùn)行時(shí)也是通過一個(gè)全局的hash表存儲了對象以及所有指向它的弱引用的指針的映射關(guān)系掐场。
    變量(可以理解為OC對象指針)與對象的關(guān)系可以其實(shí)就只有三種:強(qiáng)引用,弱引用以及無關(guān)系贩猎。強(qiáng)引用理解為持有對象會影響對象的生命周期熊户,弱引用理解為指向?qū)ο蟮粫绊憣ο蟮纳芷冢瑹o關(guān)系就是變量與對象沒有任何聯(lián)系比如3歲的小明和沙灘上的一粒沙子吭服。在OC里對應(yīng)的用于表達(dá)強(qiáng)引用和弱引用就是用__strong 和 __weak修飾符來表達(dá)嚷堡。強(qiáng)引用和弱引用貌似已經(jīng)可以完整表達(dá)指針變量和對象的關(guān)系了,但實(shí)際使用過程中發(fā)現(xiàn)還是不夠用艇棕,出現(xiàn)了一些其他變量與對象關(guān)系修飾符蝌戒,比如 __autoreleasing,__unsafe_unretain沼琉。下面就具體說下這幾個(gè)修飾符的作用以及與引用計(jì)數(shù)的關(guān)系
    __strong:指針變量的默認(rèn)修飾符北苟,如下代碼
id obj = [[NSObject alloc] init];

與下面的代碼等價(jià):

id __strong obj = [[NSObject alloc] init];

__strong修飾的變量指向(賦值)對象時(shí)會將對象的引用計(jì)數(shù)加一,類似MRC下調(diào)用有一次[obj retain]打瘪,在指針變量出了它作用域后會通過調(diào)用對象的release方法將對象引用計(jì)數(shù)減一友鼻,如果對象的引用計(jì)數(shù)為0了,就會調(diào)用對象的dealloc方法(我們項(xiàng)目有個(gè)很簡單的檢查VC是否有內(nèi)存泄露的方法闺骚,就是判斷VC的dealloc方法在pop后的2秒之內(nèi)有沒有調(diào)用彩扔,如果沒有就彈框提示,不知道各位有沒有其他更高級的方法)葛碧。在MRC下借杰,在對象在出了它作用域的時(shí)候需要主動調(diào)用下 [obj release],在ARC就不需要鍵入這行代碼(手寫也通不過編譯)进泼,因?yàn)榫幾g器在ARC下會自動在合適的地方插入這句[obj release]蔗衡∠怂洌總結(jié)下:__strong 修飾的變量保證訪問期間對象 一直存在

__weak修飾的變量在指向?qū)ο蟮臅r(shí)候绞惦,對象的引用計(jì)數(shù)不會發(fā)生變化逼纸,它存在的主要目的是解決循環(huán)引用的問題。如下代碼(ARC下济蝉,如無特殊說明杰刽,本文貼出的代碼均指ARC環(huán)境下):

@interface TestA : NSObject
@property (nonatomic, strong) id obj;//生成的成員變量使用__strong修飾
@end
@interface TestB : NSObject
@property (nonatomic, strong) id obj;//生成的成員變量使用__strong修飾
@end
int main() {
TestA *a = [[TestA alloc] init];    //a指向?qū)ο蟮囊糜?jì)數(shù)為1
TestB *b = [[TestB alloc] init];    //b指向?qū)ο蟮囊糜?jì)數(shù)為1
a.obj = b;    // 由于a的obj是強(qiáng)引用,根據(jù)上面說的規(guī)則b指向?qū)ο蟮囊糜?jì)數(shù)為2
b.obj = a;    // 由于b的obj是強(qiáng)引用王滤,根據(jù)上面說的規(guī)則a指向?qū)ο蟮囊糜?jì)數(shù)為2贺嫂,出現(xiàn)了循環(huán)引用
}

出了main函數(shù)的作用域后,a和b變量均失效雁乡,這個(gè)時(shí)候會調(diào)用[a release]第喳,[b release];方法,這個(gè)時(shí)候a和b指向的對象引用計(jì)數(shù)均減1踱稍,完了后都為1曲饱。但是這個(gè)時(shí)候a和b指向的對象均無法再次調(diào)用release方法進(jìn)行釋放了,因?yàn)橥饷鏇]機(jī)會接觸到他們了珠月,他們的引用計(jì)數(shù)又不為零扩淀,所謂的內(nèi)存泄露就來了。啤挎。驻谆。值得一提的是在對象釋放后,weak變量自動被賦值為nil侵浸。大致流程如下:

  1. 在weak表中找到鍵值為對象地址的記錄(目測應(yīng)該是個(gè)鏈表)
  2. 然后逐個(gè)將這些weak變量賦值位置為nil
  3. 完了后將改鍵值從weak表中刪除
  4. 從引用計(jì)數(shù)表中刪除該對象鍵值對應(yīng)的記錄
    需要注意的是雖然書上說每使用一次weak變量會把對象在自動釋放池里就注冊一次旺韭,而且在往自動釋放池中注冊的時(shí)候?qū)ο蟮囊糜?jì)數(shù)會也加一,但是實(shí)際上這兩條都沒有出現(xiàn)(Xcode8.3.1掏觉,sdk 10.3)区端,具體見下圖:
weak并沒有將對象注冊到自動釋放池中代碼

輸出結(jié)果

總結(jié)下:__weak修飾符主要用于打破循環(huán)引用

__autoreleaseing表示將對象放入自動釋放池中,等價(jià)于MRC下的[obj autorelease]方法澳腹,在自動釋放池里清理對象的時(shí)候?qū)ο蟮囊糜?jì)數(shù)減一织盼,如果對象計(jì)數(shù)為零則廢棄并調(diào)用對象的dealloc方法〗此總結(jié):__autoreleaseing最終效果是延長了對象的生命周期沥邻,將對象的釋放(對自動釋放池里的對象調(diào)用[obj release])時(shí)機(jī)改為runloop結(jié)束時(shí)。主要用于函數(shù)返回對象和引用參數(shù)的傳遞羊娃。函數(shù)返回對象如下:

\+ (id)myObject {
NSObject *obj = [[NSObject alloc] init];//1
return obj;//2
}

步驟1里生成一個(gè)自己持有的對象obj唐全,其引用計(jì)數(shù)為1,步驟2強(qiáng)這個(gè)obj放回,那個(gè)obj會被放入自動釋放池中邮利,也就是會調(diào)用一次[obj autorelease];那么obj的引用計(jì)數(shù)為2弥雹,由于obj要出自己的作用域了(函數(shù)體),其會調(diào)用一次[obj release]延届,這個(gè)時(shí)候obj的引用計(jì)數(shù)為1剪勿。如下代碼:

id temp = [MyObject myObject];

將對象obj作為函數(shù)的返回值賦給temp(蘋果做了很多優(yōu)化,可以先不用管)方庭,可以簡單認(rèn)為就是將對象傳遞給了調(diào)用方厕吉,temp所指向的對象引用計(jì)數(shù)為1(這里面還有很多細(xì)節(jié),大家可以繼續(xù)去那本書里面挖械念,或者一起套討論)头朱。
對于__autoreleaseing還要注意一點(diǎn)是: 對象加入自動釋放池一次,對象的引用計(jì)數(shù)就+1. 如下代碼可以有驗(yàn)證:

autorelease改變對象的引用計(jì)數(shù)

最后說下__unsafe_unretain订讼。這個(gè)可以理解為對象廢棄后不會置nil的weak就可以了髓窜,現(xiàn)在基本用不到了扇苞,可以暫時(shí)不用管欺殿。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鳖敷,隨后出現(xiàn)的幾起案子脖苏,更是在濱河造成了極大的恐慌,老刑警劉巖定踱,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棍潘,死亡現(xiàn)場離奇詭異,居然都是意外死亡崖媚,警方通過查閱死者的電腦和手機(jī)亦歉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來畅哑,“玉大人肴楷,你說我怎么就攤上這事≤牛” “怎么了赛蔫?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長泥张。 經(jīng)常有香客問我呵恢,道長,這世上最難降的妖魔是什么媚创? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任渗钉,我火速辦了婚禮,結(jié)果婚禮上钞钙,老公的妹妹穿的比我還像新娘鳄橘。我一直安慰自己粤剧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布挥唠。 她就那樣靜靜地躺著抵恋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宝磨。 梳的紋絲不亂的頭發(fā)上弧关,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機(jī)與錄音唤锉,去河邊找鬼世囊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛窿祥,可吹牛的內(nèi)容都是我干的株憾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼晒衩,長吁一口氣:“原來是場噩夢啊……” “哼嗤瞎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起听系,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤贝奇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后靠胜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掉瞳,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年浪漠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陕习。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡址愿,死狀恐怖该镣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情必盖,我是刑警寧澤拌牲,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站歌粥,受9級特大地震影響塌忽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜失驶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一土居、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦擦耀、人聲如沸棉圈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽分瘾。三九已至,卻和暖如春吁系,著一層夾襖步出監(jiān)牢的瞬間德召,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工汽纤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留上岗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓蕴坪,卻偏偏與公主長得像肴掷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子背传,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容