一.內(nèi)存管理 /引用計(jì)數(shù)
Objective-C 中的內(nèi)存管理遵绰,也就是引用計(jì)數(shù)
1.1內(nèi)存管理的思考方式
- 自己生成的對(duì)象沸伏,自己持有
- 非自己生成的對(duì)象莫湘,自己也能持有
- 不再需要自己持有的對(duì)象時(shí)釋放
- 非自己持有的對(duì)象無(wú)法釋放
注意: "自己"是指"對(duì)象的使用環(huán)境"变逃,以上內(nèi)存管理思考方式在MRC與ARC下都是可行的贝搁,只是源代碼的記述方法稍有不同吗氏,可以從后文中所述ARC中追加的所有權(quán)聲明開(kāi)始理解。
在objective-c內(nèi)存管理中雷逆,"生成"弦讽,"持有","釋放"和"廢棄"四個(gè)詞頻繁出現(xiàn)膀哲,這些內(nèi)存管理方法不包括在該語(yǔ)言中往产,而是由Cocoa框架中Foundation框架類庫(kù)的NSObject類擔(dān)負(fù)內(nèi)存管理的職責(zé)
1.1.1 自己生成的對(duì)象,自己持有
使用以下名稱開(kāi)頭的方法意味著自己生成的對(duì)象只有自己持有
- alloc
- new
- copy
- mutableCopy
此外某宪,使用駝峰寫法(CamelCase)命名的下列名稱也意味著自己生成并持有對(duì)象 - allocMyObject
- newThatObject
- copyThis
- mutableCopyYourObject
1.1.2 非自己生成的對(duì)象仿村,自己也能持有
用上述之外方法取得的對(duì)象,因?yàn)榉亲约荷刹⒊钟行宋梗宰约翰皇窃搶?duì)象的持有者蔼囊,如NSMutableArray類的 array類方法。
通過(guò)使用retain方法衣迷,非自己生成的對(duì)象跟用第一類方法生成并持有的對(duì)象一樣畏鼓,成為了自己所持有的。
1.1.3 不再需要自己持有的對(duì)象時(shí)釋放
自己持有的對(duì)象壶谒,一旦不再需要云矫,持有者有義務(wù)釋放該對(duì)象,釋放使用release方法汗菜。
用alloc方法由自己生成并持有的對(duì)象通過(guò)release方法釋放
自己生成而非自己持有的對(duì)象让禀,若用retain方法變?yōu)樽约撼钟校瑯涌梢杂胷elease方法釋放
-
用某個(gè)方法生成對(duì)象陨界,并將其返還給該方法的調(diào)用方巡揍,這種情況下:
-
原封不動(dòng)地返回alloc方法生成并持有的對(duì)象,就能讓調(diào)用方也持有該對(duì)象(被調(diào)用方法名符合alloc等方法命名規(guī)則)
- (id)allocObject { //自己生成并持有對(duì)象 id obj =[ [NSObject alloc] init]; //自己持有對(duì)象 return obj普碎; } // 取得非自己生成并持有的對(duì)象 id obj1 = [ obj0 allocObject]; 自己持有對(duì)象
-
調(diào)用 [NSMutableArray array]方法取得的對(duì)象存在吼肥,但自己不持有對(duì)象录平,又是如何實(shí)現(xiàn)的呢
- (id)object {
id obj = [[NSObject alloc] init];
//自己持有對(duì)象
[obj autorelease];
//取得的對(duì)象存在麻车,但自己不持有對(duì)象
return obj缀皱;
}
id obj1 = [obj0 object];
//取得的對(duì)象存在,但自己不持有對(duì)象动猬,當(dāng)然也能夠通過(guò)retain方法調(diào)用autorelease方法取得的對(duì)象變?yōu)樽约撼钟衅《罚缦?br> [obj1 retain];
//自己持有對(duì)象
autorelease提供這樣的功能,使對(duì)象在超出指定的生存范圍時(shí)能夠自動(dòng)并正確的釋放
1.1.4 無(wú)法釋放非自己持有的對(duì)象
- 自己生成并持有對(duì)象后赁咙,在釋放完不再需要的對(duì)象之后再次釋放
- 取得的對(duì)象存在钮莲,但自己不持有對(duì)象時(shí)釋放
1.2 autorelease
看上去很像ARC,實(shí)際上更類似C語(yǔ)言中自動(dòng)變量(局部變量)的特性:
{
int a;
}
//因?yàn)槌鲎兞孔饔糜虮怂詣?dòng)變量"int a"被廢棄崔拥,不可再訪問(wèn)
autorelease會(huì)像c語(yǔ)言自動(dòng)變量這樣來(lái)對(duì)待對(duì)象實(shí)例凤覆。當(dāng)超出其作用域時(shí),對(duì)象實(shí)例的release實(shí)例方法被調(diào)用盯桦。不同的是,可以設(shè)定變量的作用域拥峦。
autorelease具體使用方法如下:
- 生成并持有NSAutoreleasePool對(duì)象贴膘;
- 調(diào)用已分配對(duì)象的autorelease實(shí)例方法;
- 廢棄NSAutoreleasePool對(duì)象刑峡。
NSAutoreleasePool對(duì)象的生命周期相當(dāng)于c語(yǔ)言變量的作用域玄柠。對(duì)于所有調(diào)用過(guò)autorelease實(shí)例方法的對(duì)象氛琢,在廢棄NSAutoreleasePool對(duì)象時(shí),都將調(diào)用release實(shí)例方法随闪。
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain]; //等同于[obj release];
在Cocoa框架中阳似,相當(dāng)于程序主循環(huán)的NSRunLoop或者在其他程序可運(yùn)行的地方铐伴,對(duì)NSAutoreleasePool對(duì)象進(jìn)行生成、持有和廢棄處理畜吊。所以應(yīng)用程序開(kāi)發(fā)者不一定非得使用NSAutoreleasePool對(duì)象來(lái)進(jìn)行開(kāi)發(fā)工作户矢。
盡管如此,在大量產(chǎn)生autorelease對(duì)象時(shí),只要不廢棄NSAutoreleasePool對(duì)象捌年,那么生成的對(duì)象就不能被釋放,因此有時(shí)會(huì)產(chǎn)生內(nèi)存不足的現(xiàn)象眠砾。典型的例子時(shí)讀入大量圖像的同時(shí)改變其尺寸托酸。圖像文件讀入到NSData對(duì)象,并從中生成UIImage對(duì)象励堡,改變?cè)搶?duì)象尺寸后生成新的UIImage對(duì)象,這種情況下會(huì)大量產(chǎn)生autorelease對(duì)象淤井。
for(int i = 0; i<圖像數(shù)摊趾;++i) {
/*
*讀入圖像
*大量產(chǎn)生autorelease的對(duì)象
*由于沒(méi)有廢棄NSAutoreleasePool對(duì)象
*最終導(dǎo)致內(nèi)存不足
*/
}
在此情況下,有必要在適當(dāng)?shù)牡胤缴衫恪⒊钟谢驈U棄NSAutoreleasePool對(duì)象。
for(int i = 0; i<圖像數(shù)止吐;++i) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
/*
*讀入圖像
*大量產(chǎn)生autorelease的對(duì)象
*/
[pool drain];
/*
*通過(guò)[pool drain],
*autorelease
*/
}
二. ARC規(guī)則
實(shí)際上"引用計(jì)數(shù)式內(nèi)存管理"的本質(zhì)部分在ARC中并沒(méi)有改變侨糟,ARC只是自動(dòng)地幫助我們處理"引用計(jì)數(shù)"的相關(guān)部分。
2.1所有權(quán)修飾符
ARC有效時(shí)秕重,id類型和對(duì)象類型同c語(yǔ)言其他類型不同,其類型上必須附加所有權(quán)修飾符二拐。所有權(quán)修飾符一共有4種凳兵。
- __strong修飾符
- __weak修飾符
- __unsafe_unretained修飾符
- __autoreleasing修飾符
2.1.1 __strong修飾符
_ _strong修飾符是id類型和對(duì)象類型默認(rèn)的所有權(quán)修飾符。在沒(méi)有明確指定所有權(quán)修飾符時(shí)庐扫,id和對(duì)象類型默認(rèn)為_(kāi)_strong修飾符仗哨。
以下兩種代碼表意相同
id obj = [[NSObject alloc] init];
id __strong obj = [[NSObject alloc] init];
如"strong"名稱所示铅辞,附有 _ _strong修飾符表示對(duì)對(duì)象的"強(qiáng)引用",附有 __strong修飾符的變量obj在超出其變量作用域時(shí)巷挥,即在該變量被廢棄時(shí)验靡,會(huì)釋放其被賦予的對(duì)象,如下:
1. 自己生成并持有對(duì)象
{
//自己生成并持有對(duì)象
id __strong obj = [[NSObject alloc] init];
//因?yàn)樽兞縪bj為強(qiáng)引用高职,所以自己持有對(duì)象
}
//因?yàn)樽兞縪bj超出其作用域辞州,強(qiáng)引用失效
//所以自動(dòng)地釋放自己持有的對(duì)象。
//對(duì)象的所有者不存在变过,因此廢棄該對(duì)象
2.取得非自己生成并持有的對(duì)象
{
//取得非自己生成并持有的對(duì)象
id __strong obj = [NSMutableArray array];
//因?yàn)樽兞縪bj為強(qiáng)引用,所以自己持有對(duì)象
}
//因?yàn)樽兞縪bj超出其作用域媚狰,強(qiáng)引用實(shí)效,
//所以自動(dòng)地釋放自己持有的對(duì)象
附有__strong修飾符的變量之間可以相互賦值沧踏。
舉例暫略
即便是objective-c類成員變量,也可以在方法參數(shù)上遗锣,使用附有 _strong修飾符的變量
舉例暫略
另外嗤形,__strong修飾符通后面要講的__weak修飾符合__autorelease修飾符一起,可以保證獎(jiǎng)附有這些修飾符的自動(dòng)變量初始化為nil赋兵。
2.1.2 __weak修飾符
僅通過(guò)__strong修飾符是不能解決有些重大問(wèn)題的,帶有__strong修飾符的成員變量在持有對(duì)象時(shí)拓轻,很容易發(fā)生循環(huán)引用经伙。循環(huán)引用容易發(fā)生內(nèi)存泄漏勿锅。所謂內(nèi)存泄漏就是應(yīng)當(dāng)廢棄的對(duì)象在超出其生存期后繼續(xù)存在枣氧。
使用__weak修飾符可以避免循環(huán)引用溢十。__weak修飾符與__strong修飾符相反达吞,提供弱引用。弱引用不能持有對(duì)象實(shí)例吞鸭,所以在超出其變量作用域時(shí)覆糟,對(duì)象即被釋放。
舉例待補(bǔ)滩字。。麦箍。
__weak修飾符還有另一有點(diǎn)挟裂,在持有某對(duì)象的弱引用時(shí),若該對(duì)象被廢棄话瞧,則此弱引用將自動(dòng)失效且處于nil被賦值的狀態(tài),通過(guò)檢查附有__weak修飾符的變量是否為nil划滋,可以判斷被賦值的對(duì)象是否已廢棄埃篓。
2.1.3__unsafe_unretained修飾符
__unsafe_unretained修飾符正如其名unsafe所示,是不安全的所有權(quán)修飾符架专。盡管ARC式的內(nèi)存管理是編譯器的工作,但附有__unsafe_unretained修飾符的變量不屬于編譯器的內(nèi)存管理對(duì)象想邦。
附有__unsafe_unretained修飾符的變量同附有_weak修飾符的變量一樣委刘,因?yàn)樽约荷刹⒊钟械膶?duì)象不能繼續(xù)為自己所有鹰椒,所以生成的對(duì)象會(huì)立即被釋放呕童。
舉例很重要,待補(bǔ)充奸汇。往声。。
在使用__unsafe_unretained修飾符時(shí)烁挟,賦值給附有__strong 修飾符的變量時(shí)有必要確保被賦值的對(duì)象卻是存在骨坑。使用__unsafe_unretained修飾符變量的必要性:在iOS4的應(yīng)用程序中,必須使用__unsafe_unretained修飾符來(lái)替代__weak修飾符且警。賦值給附有__unsafe_unretained修飾符變量的對(duì)象在通過(guò)該變量使用時(shí)礁遣,如果沒(méi)有確保其確實(shí)存在,那么應(yīng)用程序就會(huì)崩潰杏头。
2.1.4__autorealeasing修飾符
ARC有效時(shí)
不能使用autorelease方法
-
不能使用NSAutoreleasePool類
這樣以來(lái)沸呐,雖然autorelease無(wú)法直接使用,但ARC有效時(shí)autorelease功能是起作用的崭添。//ARC無(wú)效 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; id obj = [[NSObject alloc] init]; [obj autorelease]; [pool drain]; //ARC有效時(shí),該源碼能寫成下面這樣: @autoreleasepool { id __autoreleasing obj = [[NSObject alloc] init]; }
指定"@ autoreleasepool塊"來(lái)替代"NSAutoreleasePool"類對(duì)象生成棘伴、持有即廢棄"這一范圍屁置,但是,顯式地附加__autoreleasing修飾符同顯式地附加__strong修飾符一樣罕見(jiàn)淳地,autoreleasing不同于autorelease,它用于按引用傳遞對(duì)象
@ autoreleasepool圖片表述待補(bǔ)
@ autoreleasepool四個(gè)非顯式使用_autoreleasing修飾符的例子
2.2規(guī)則
- 不能使用retain/release/retainCount/autorelease
- 不能使用NSAllocateObject/NSDeallocateObject
- 須遵守內(nèi)存管理的方法命名規(guī)則
- 不要顯式調(diào)用dealloc
- 使用@autoreleasepool塊替代NSAutoreleasePool
- 不能使用區(qū)域(NSZone)
- 對(duì)象型變量不能作為c語(yǔ)言結(jié)構(gòu)體(struct/union)的成員
- 顯式轉(zhuǎn)換"id"和"void*"
2.2.1 不能使用retain/release/retainCount/autorelease
2.2.2 不能使用NSAllocateObject/NSDeallocateObject
2.2.3 須遵守內(nèi)存管理的方法命名規(guī)則
在ARC無(wú)效時(shí)伍伤,用于對(duì)象生成/持有的方法必須遵守以下命名規(guī)則
alloc/new/copy/mutableCopy遣钳,上述名稱開(kāi)始的方法在返回對(duì)象時(shí),必須返回給調(diào)用房所應(yīng)當(dāng)持有的對(duì)象蕴茴。這在ARC有效時(shí)也一樣,返回的對(duì)象完全沒(méi)有改變蒋畜。只是在ARC有效時(shí)要追加一條命名規(guī)則撞叽。
-
init
以init開(kāi)始的方法的規(guī)則要比alloc/new/copy/mutableCopy更嚴(yán)格。該方法必須時(shí)實(shí)例方法科展,并且必須要返回對(duì)象糠雨。返回的對(duì)象應(yīng)為id類型或該方法聲明類的對(duì)象類型,抑或是該類的超類型或子類型甘邀。該返回對(duì)象并不注冊(cè)到autoreleasepool上∥肭伲基本上只是對(duì)alloc方法返回值的對(duì)象進(jìn)行初始化處理病返回該對(duì)象测摔。
以init開(kāi)始的方法的命名規(guī)則:- (id)initWithObject:(id)obj; //可以
- (void)initThisObject; //遵守了命名規(guī)則,但雖然以init開(kāi)始锋八,卻沒(méi)有返回對(duì)象挟纱,不能使用
- (void)initialize; //不包含在命名規(guī)則里
2.2.4 不要顯式調(diào)用dealloc
對(duì)象被廢棄時(shí),不管ARC是否有效紊服,都會(huì)調(diào)用對(duì)象的dealloc方法
dealloc方法在大多數(shù)情況下還適用于刪除已注冊(cè)的代理或觀察者對(duì)象
2.2.5 使用@autoreleasepool塊替代NSAutoreleasePool
2.2.6不能使用區(qū)域(NSZone)
2.2.7對(duì)象型變量不能作為c語(yǔ)言結(jié)構(gòu)體(struct/union)的成員
2.2.8顯式轉(zhuǎn)換"id"和"void*"
2.3 屬性
屬性時(shí)在Objective-c2.0引入的,提供了一種輕松地為類聲明實(shí)例變量的方式参萄,并讓編譯器自動(dòng)生成存取方法。
當(dāng)ARC有效時(shí)校赤,以下可作為這種屬性聲明中使用的屬性來(lái)用