現(xiàn)在我們使用Objective-C編寫iOS和Mac OS App的時(shí)候都是使用的是ARC來(lái)進(jìn)行內(nèi)存管理的。用一句話來(lái)總結(jié)ARC的功能的話窃这,就是ARC使用編譯器來(lái)代替程序員做內(nèi)存管理的工作轿偎。雖然編譯器幫我們做了內(nèi)存管理工作分扎,但是我們應(yīng)該弄清楚編譯器幫我們做了哪些工作猖闪,哪些工作是編譯器無(wú)法做的,還是需要我們程序員來(lái)完成的暖哨。
OC中對(duì)象的內(nèi)存管理是基于引用計(jì)數(shù)的內(nèi)存管理:(Reference Counted Memory Management)
我們可以想象一下我們?cè)谙到y(tǒng)中使用對(duì)象的情形赌朋,在需要的時(shí)候向系統(tǒng)去申請(qǐng)內(nèi)存,不需要的時(shí)候釋放對(duì)象內(nèi)存篇裁,還給系統(tǒng)沛慢。如果長(zhǎng)期霸占著不需要的內(nèi)存而不釋放,那就是刷流氓达布,會(huì)造成系統(tǒng)的內(nèi)存泄露团甲。內(nèi)存泄露的多了,系統(tǒng)有可能會(huì)崩潰往枣。
內(nèi)存管理 == 引用計(jì)數(shù)伐庭,如果我們明白了引用計(jì)數(shù)的原理和使用的規(guī)則,OC的內(nèi)存管理就變的簡(jiǎn)單了分冈。
對(duì)象使用引用計(jì)數(shù)方法使得內(nèi)存管理變的很簡(jiǎn)單。
我們需要弄清楚兩個(gè)問題:1. 引用計(jì)數(shù)的對(duì)象是什么霸株?2. 引用計(jì)數(shù)是如何變化的雕沉?
引用計(jì)數(shù)的對(duì)象是什么:這個(gè)問題很簡(jiǎn)單,在這里我們討論的引用計(jì)數(shù)的對(duì)象就是Objective-C對(duì)象去件。
引用計(jì)數(shù)是如何變化的:要想回答這個(gè)問題坡椒,我們需要先回答另一個(gè)問題,就是有哪些操作會(huì)影響引用計(jì)數(shù)的變化尤溜?
影響引用計(jì)數(shù)的變化的操作倔叼,大致有以下4種:
- 創(chuàng)建對(duì)象從而獲得對(duì)象的所有權(quán)(have ownership),引用計(jì)數(shù)+1宫莱, 從0變成1
- 持有對(duì)象從而取得對(duì)象的所有權(quán)(take ownership)丈攒,引用計(jì)數(shù)+1
- 釋放對(duì)象從而放棄對(duì)象的所有權(quán)(relinguish ownership),引用計(jì)數(shù)-1授霸;
- 如果一個(gè)對(duì)象的引用計(jì)數(shù)-1后為0巡验,意味著這個(gè)對(duì)象不存在所有權(quán)關(guān)系,沒有被其它對(duì)象需要的可能碘耳,也就沒有存在的意義显设,系統(tǒng)會(huì)銷毀(destroy)對(duì)象回收對(duì)象分配的內(nèi)存。這個(gè)銷毀操作是系統(tǒng)自動(dòng)進(jìn)行的辛辨。
我們可以發(fā)現(xiàn)一個(gè)共同點(diǎn)捕捂,上面的這些操作都是針對(duì)對(duì)象的所有權(quán)(ownership)進(jìn)行的瑟枫,對(duì)象的所有權(quán)可以看作是操作對(duì)象的憑證或者授權(quán),沒有對(duì)象的所有權(quán)是不能對(duì)該對(duì)象進(jìn)行操作的指攒。
所以要想操作一個(gè)對(duì)象力奋,首先必要要獲得對(duì)象的所有權(quán),獲得所有權(quán)的方法有兩種:
- 如果這個(gè)對(duì)象已經(jīng)存在了幽七,我們只需要聲明持有這個(gè)對(duì)象來(lái)獲得對(duì)象的所有權(quán)景殷,同時(shí)對(duì)象的引用計(jì)數(shù)會(huì)+1
- 如果對(duì)象不存在,我們需要新創(chuàng)建一個(gè)對(duì)象澡屡,同時(shí)自然會(huì)獲得這個(gè)新建對(duì)象的所有權(quán)猿挚,新建對(duì)象的引用計(jì)數(shù)從0變成1。
對(duì)于一個(gè)我們沒有所有權(quán)的對(duì)象驶鹉,是不能進(jìn)行操作的绩蜻,如果不小心操作了一個(gè)沒有所有權(quán)的對(duì)象,程序有可能會(huì)崩潰室埋。
OC中有相應(yīng)的方法來(lái)進(jìn)行對(duì)象所有權(quán)操作的方法:
對(duì)象所有權(quán)操作描述 OC方法
新建對(duì)象獲得對(duì)象的所有權(quán) alloc/new/copy/mutableCopy
取得已有對(duì)象的所有權(quán) retain
放棄對(duì)象的所有權(quán) release
銷毀對(duì)象(系統(tǒng)自動(dòng)調(diào)用) dealloc
內(nèi)存管理需要遵循的幾個(gè)規(guī)則:
- 能夠獲得任何新創(chuàng)建對(duì)象的所有權(quán):使用alloc/new/copy/mutableCopy開頭的方法來(lái)新建對(duì)象办绝,并取得對(duì)象的所有權(quán)。我用四個(gè)字簡(jiǎn)單總結(jié)就是你建你有姚淆。
- 能夠取得其它對(duì)象(不是自己創(chuàng)建的)的所有權(quán):可以使用retain方法取得對(duì)象的所有權(quán)來(lái)操作對(duì)象孕蝉。對(duì)于不是以alloc/new/copy/mutableCopy開頭的方法返回得到的對(duì)象,雖然對(duì)象存在腌逢,但是我們沒有該對(duì)象的所有權(quán)降淮,不能對(duì)它進(jìn)行操作。只有通過retain方法取得對(duì)象的所有權(quán)后搏讶,才能進(jìn)行相應(yīng)的操作佳鳖。簡(jiǎn)單總結(jié)就是他建你有。
- 及時(shí)放棄不需要的對(duì)象的所有權(quán):當(dāng)一個(gè)對(duì)象不需要時(shí)媒惕,不管是通過新建對(duì)象還是使用retain方法獲得的對(duì)象所有權(quán)系吩,都必須要調(diào)用release方法來(lái)放棄對(duì)該對(duì)象的所有權(quán)。簡(jiǎn)單來(lái)說(shuō)就是沒用快扔妒蔚。
- 不能放棄你沒有所有權(quán)的對(duì)象:對(duì)于沒有所有權(quán)的對(duì)象穿挨,不能調(diào)用release方法,一旦調(diào)用了release方法面睛,程序就有可能出錯(cuò)奔潰絮蒿。簡(jiǎn)單來(lái)說(shuō)就是沒有別動(dòng)。
如果想要進(jìn)一步弄清楚OC對(duì)象的內(nèi)存管理規(guī)則叁鉴,必須要清楚幾個(gè)問題和細(xì)節(jié):
- 引用一個(gè)對(duì)象和持有一個(gè)對(duì)象的區(qū)別土涝?或者說(shuō)獲得對(duì)象和獲得對(duì)象所有權(quán)的區(qū)別?
面向?qū)ο蟮闹R(shí)告訴我們幌墓,一個(gè)對(duì)象的引用其實(shí)就是對(duì)象在內(nèi)存中的地址但壮,通過引用我們可以找到我們需要的對(duì)象的位置冀泻。OC中的內(nèi)存管理告訴我們,持有一個(gè)對(duì)象是指我們不僅能夠知道對(duì)象在內(nèi)存中的位置蜡饵,而且我們還擁有對(duì)象的所有權(quán)弹渔,有了獲得了對(duì)象的所有權(quán)后,我們能夠操作這個(gè)對(duì)象溯祸。
舉個(gè)不太恰當(dāng)?shù)睦樱罕热缯f(shuō)你暗戀一個(gè)小姐姐肢专,同時(shí)你也知道了小姐姐的住址,但是小姐姐并沒有同意和你在一起焦辅,此時(shí)你只是有了小姐姐的地址博杖,然而并不能對(duì)小姐姐做什么;如果你不僅知道了小姐姐的住址筷登,小姐姐也喜歡你同意和你在一起了剃根,此時(shí)你獲得了小姐姐的所有權(quán)(不太恰當(dāng)?shù)恼f(shuō)法),然后你就能對(duì)小姐姐進(jìn)行一番操作了前方。這就是引用一個(gè)對(duì)象和持有一個(gè)對(duì)象的區(qū)別狈醉。
下面使用一些代碼例子來(lái)說(shuō)明內(nèi)存管理和引用計(jì)數(shù):
Example 1: 創(chuàng)建對(duì)象并獲得對(duì)象所有權(quán)
// 使用alloc方法,并獲得對(duì)象所有權(quán)
id obj1 = [[NSObject alloc] init];
// 使用new方法惠险,并獲得對(duì)象所有權(quán)
id obj2 = [NSObject new];
Example 2: 不是自己創(chuàng)建的對(duì)象苗傅,獲得對(duì)象所有權(quán)
// 獲得一個(gè)不是自己創(chuàng)建的對(duì)象
id obj1 = [NSMutableArray array];
// 獲得對(duì)象的引用,但是沒有獲得對(duì)象的所有權(quán)
[obj1 retain];
//獲得了對(duì)象的所有權(quán)
Example 3: 不需要的對(duì)象莺匠,快快放棄對(duì)象的所有權(quán)
//創(chuàng)建對(duì)象并獲得對(duì)象所有權(quán)
id obj = [[NSObject alloc] init];
//獲得對(duì)象的所有權(quán)金吗,可以操作對(duì)象
...
[obj release];
// 放棄對(duì)象的所有權(quán),但是變量obj還是指向?qū)ο笕たⅲ磳?duì)象的引用。但是不能訪問對(duì)象和操作對(duì)象旱物。
Example 4: 方法返回對(duì)象時(shí)的所有權(quán)關(guān)系
//假設(shè)allocMyObject和myObject方法存在于MyClass中
- (id)allocMyObject {
//新建對(duì)象并獲得所有權(quán)
id obj = [[NSObject alloc] init];
// 方法取得對(duì)象的所有權(quán)
return obj;
//返回對(duì)象遥缕,并轉(zhuǎn)把對(duì)象的所有權(quán)轉(zhuǎn)移到方法調(diào)用者
}
- (id)myObject {
//新建對(duì)象并獲得所有權(quán)
id obj = [[NSObject alloc] init];
[obj autorelease];
//方法放棄對(duì)象的所有權(quán)
return obj;
//返回對(duì)象,方法調(diào)用者無(wú)法取得對(duì)象的所有權(quán)
}
//myObj是MyClass的一個(gè)實(shí)例對(duì)象
//通過alloc開頭的方法創(chuàng)建的對(duì)象宵呛,獲得對(duì)象的所有權(quán)
id obj1 = [myObj allocMyObject];
//獲得對(duì)象的引用单匣,但是沒有取得對(duì)象的所有權(quán)
id obj2 = [myObj myObject];
Example 5: 不能操作沒有所有權(quán)的對(duì)象
// 新建一個(gè)對(duì)象并取得對(duì)象的所有權(quán)
id obj1 = [[NSObject alloc] init];
//具有對(duì)象的所有權(quán)
[obj1 release];
//放棄對(duì)象的所有權(quán),此時(shí)不能操作對(duì)象
[obj1 release];
// 操作了一個(gè)沒有所有權(quán)的對(duì)象宝穗,系統(tǒng)可能崩潰
// 獲得一個(gè)對(duì)象的引用户秤,但沒有取得對(duì)象的所有權(quán)
id obj2 = [myObj myObject];
// 此時(shí)沒有對(duì)象的所有權(quán)
[obj2 release];
// 操作了一個(gè)沒有所有權(quán)的對(duì)象,系統(tǒng)可能崩潰
總結(jié)
可以用一個(gè)狗和繩圈的比喻來(lái)描述對(duì)象引用計(jì)數(shù)的過程變化:狗脖子要有繩圈才能上街逮矛。當(dāng)需要帶狗上街時(shí)鸡号,你就應(yīng)該給它套上一個(gè)繩圈,表明你取得了這個(gè)狗的所有權(quán)须鼎。當(dāng)別人看上了你的狗鲸伴,他也可以給這個(gè)狗加上一個(gè)繩圈府蔗,表明也取得了這個(gè)狗的所有權(quán)。只要狗的脖子上面還有繩圈汞窗,說(shuō)明這個(gè)狗是有人所有的姓赤,或者說(shuō)是有需求的,這時(shí)候這個(gè)狗是跑不掉的仲吏。當(dāng)別人不需要這個(gè)狗時(shí)不铆,就會(huì)解開他擁有的繩圈,表明放棄了對(duì)這個(gè)狗的所有權(quán)裹唆。當(dāng)所有人都解開了繩圈誓斥,放棄對(duì)這個(gè)狗的所有權(quán)時(shí)。狗的脖子上是沒有繩圈的品腹,這時(shí)狗子一看岖食,沒有繩圈套著了,趕緊溜吧舞吭。這里的??就相當(dāng)于一個(gè)對(duì)象泡垃,脖子上的繩圈就是引用計(jì)數(shù)。
引用計(jì)數(shù)的需要遵循的規(guī)則:
- 你建你有
- 他建你有
- 沒用快扔
- 沒有別動(dòng)
我們還需要分清楚對(duì)象的引用和對(duì)象的所有權(quán)的區(qū)別羡鸥,想想小姐姐的故事蔑穴。
如果覺得有用可以去GitHub給個(gè)??。
參考:Pro Multithreading and Memory Management for iOS and OS X: With ARC, Grand Central Dispatch and Blocks.