內(nèi)存管理的必要性:
內(nèi)存泄漏:程序未能釋放已經(jīng)不再使用的內(nèi)存叫內(nèi)存泄漏。
懸垂指針(野指針):指針指向已經(jīng)被釋放或回收的對(duì)象癣缅。
管理內(nèi)存的三種方式:引用計(jì)數(shù)纫事,自動(dòng)引用計(jì)數(shù),自動(dòng)垃圾回收所灸。
引用計(jì)數(shù):
創(chuàng)建對(duì)象時(shí)丽惶,引用計(jì)數(shù)的初始值為1。為防止將要使用的實(shí)例對(duì)象被釋放爬立,會(huì)先向該實(shí)例對(duì)象發(fā)送retain钾唬,使其引用計(jì)數(shù)加1。
擁有實(shí)例所有權(quán)的對(duì)象叫所有者。
不再需要某個(gè)對(duì)象時(shí)抡秆,發(fā)送release奕巍,使引用計(jì)數(shù)減1.釋放內(nèi)存的不是release,而是dealloc儒士。和alloc不同的止,dealloc是實(shí)例方法。方法retainCount可以獲得對(duì)象引用計(jì)數(shù)的當(dāng)前值着撩。
在重寫(xiě)dealloc的方法中诅福,在釋放自身之前,首先要做好善后工作(釋放所有需要釋放的資源)拖叙。這些善后工作包括通過(guò)使用release放棄自身的實(shí)例變量的所有權(quán)氓润。
自動(dòng)釋放機(jī)制:
自動(dòng)釋放池的典型用法如下:
id pool = [[NSAutoreleasePool alloc] init];
/*
在此進(jìn)行一系列操作
給臨時(shí)對(duì)象發(fā)送autorelease消息。
*/
[pool release];//銷(xiāo)毀自動(dòng)釋放池薯鳍,自動(dòng)釋放池中的所有的對(duì)象也被銷(xiāo)毀咖气。
在自動(dòng)釋放池中登錄的實(shí)際上相當(dāng)于放棄了所有權(quán)的對(duì)象,被稱(chēng)為臨時(shí)對(duì)象挖滤。
使用自動(dòng)釋放池需要注意的地方:
autorelease雖然是NSObject類(lèi)的方法崩溪,但必須和類(lèi)NSAutoreleasePool一起使用。
臨時(shí)對(duì)象的生成:
除了使用alloc斩松,init方法創(chuàng)建一個(gè)對(duì)象這種標(biāo)準(zhǔn)的創(chuàng)建方法外悯舟,還有一種創(chuàng)建臨時(shí)對(duì)象的方法。通過(guò)這種方法創(chuàng)建的對(duì)象都是臨時(shí)對(duì)象砸民,生成之后會(huì)被加入到內(nèi)部的自動(dòng)釋放池。例如:Cocoa里用于處理字符串的類(lèi)NSString奋救,由UTF-8編碼的c風(fēng)格字符串生成NSString對(duì)象的方法有兩個(gè):
- (id)initWithUTF8String:(const char *)bytes
alloc生成的實(shí)例對(duì)象的初始化方法岭参,生成的實(shí)例對(duì)象的初始引用計(jì)數(shù)為1
+(id)stringWithUTF8String: ?(const chat *) ? ?bytes
生成臨時(shí)變量的類(lèi)方法,生成的實(shí)例對(duì)象會(huì)被自動(dòng)加入到自動(dòng)釋放池中尝艘。
這種類(lèi)方法的命名規(guī)則是不以init開(kāi)頭演侯,而以要生成的對(duì)象的類(lèi)型(類(lèi)名)作為開(kāi)頭。
同綜合使用alloc init創(chuàng)建對(duì)象的方法相比背亥,通過(guò)這種方法生成的對(duì)象的所有者不是調(diào)用stringWithUTF8String類(lèi)方法的對(duì)象秒际。
這種生成臨時(shí)對(duì)象的類(lèi)方法,在OC中稱(chēng)為便利構(gòu)造函數(shù)或便利構(gòu)造器狡汉。在面向?qū)ο蟮恼Z(yǔ)言中娄徊,會(huì)把生成對(duì)象的函數(shù)叫做構(gòu)造函數(shù),把在內(nèi)部調(diào)用別的構(gòu)造函數(shù)而生成的構(gòu)造函數(shù)叫做便利構(gòu)造函數(shù)盾戴。在OC中寄锐,便利構(gòu)造函數(shù)指的就是這種利用alloc,init和autorelease生成的臨時(shí)對(duì)象的類(lèi)方法。
常量對(duì)象:
內(nèi)存中的常量對(duì)象(類(lèi)對(duì)象橄仆,常量字符串對(duì)象等)的空間分配與其他對(duì)象不同剩膘,他們沒(méi)有引入計(jì)數(shù)機(jī)制永遠(yuǎn)不能釋放這些對(duì)象。給這些對(duì)象發(fā)送消息retainCount后盆顾,返回的是NSUIntegerMax(其值為0xffffffff怠褐,被定義為最大的無(wú)符號(hào)整數(shù))。
單例模式:某個(gè)類(lèi)僅能生成一個(gè)實(shí)例您宪,程序中訪問(wèn)到這個(gè)類(lèi)的對(duì)象時(shí)使用的都是同一個(gè)實(shí)例對(duì)象奈懒,在設(shè)計(jì)模式中這種情況被稱(chēng)為單例模式。Cocoa框架中有很多單例模式的應(yīng)用蚕涤,這些類(lèi)通過(guò)以shared開(kāi)頭的類(lèi)方法返回唯一的實(shí)例對(duì)象筐赔。
ARC概要:
ARC是一個(gè)編譯期技術(shù),利用此技術(shù)可以簡(jiǎn)化OC在內(nèi)存管理方面的工作量∫就現(xiàn)在ARC只能管理OC對(duì)象茴丰,不能管理通過(guò)malloc申請(qǐng)的內(nèi)存。
利用ARC編程要遵守的規(guī)則:
禁止調(diào)用引用計(jì)數(shù)相關(guān)的方法(retain天吓,release贿肩,autorelease,retainCount)龄寞。
管理自動(dòng)釋放池的新語(yǔ)法:
禁止使用NSAutoreleasePool汰规,而是使用新語(yǔ)法@autoreleasepool來(lái)管理自動(dòng)釋放池。格式如下:
@autoreleasepool ?{
}
在舊的寫(xiě)法中物邑,在自動(dòng)釋放池被初始化至被釋放期間溜哮,不可以使用break,return色解,goto等跳轉(zhuǎn)語(yǔ)句茂嗓,否則對(duì)象有可能無(wú)法被重新釋放。
而在新的語(yǔ)法中科阎,因?yàn)橐\(yùn)行到@autoreleasepool塊外的時(shí)候進(jìn)行對(duì)象的釋放述吸,所以可以使用跳轉(zhuǎn)語(yǔ)句。
另外锣笨,@autoreleasepool在非ARC模式下也能用蝌矛,并且使用@autoreleasepool比使用NSAutoreleasePool性能更好效率更高。
方法族:
同對(duì)象生成相關(guān)的方法集合叫作方法族错英。
采用引用計(jì)數(shù)方式管理內(nèi)存時(shí)入撒,如果不使用alloc/init/new/copy/mutableCopy 這些方法,或者不使用retain來(lái)保留一個(gè)對(duì)象椭岩,就不能成為對(duì)象的所有者衅金。另外噪伊,只有使用release或autorelease,才能放棄這個(gè)對(duì)象的所有權(quán)氮唯。這些規(guī)定被叫做所有權(quán)策略鉴吹。
由于ARC允許混合鏈接手動(dòng)內(nèi)存管理和自動(dòng)內(nèi)存管理的代碼,所以需要定義能夠讓編譯器明確區(qū)分的方法惩琉。
一個(gè)方法要屬于某個(gè)方法族除了需要滿足返回值和方法的類(lèi)別方面的要求外豆励,也需要滿足以下命名規(guī)則:
即選擇器的名字由方法族的名字加上非小寫(xiě)字母開(kāi)頭的字符串構(gòu)成,或選擇器通方法族名相同(開(kāi)頭的_可忽略 eg:_init:locale屬于init方法族)
目前為止一共定義了五個(gè)方法族:alloc方法族,copy方法族瞒渠,mutableCopy方法族良蒸,init方法族,new方法族伍玖。這些方法族均表示調(diào)用者對(duì)被創(chuàng)建的對(duì)象擁有所有權(quán)嫩痰。
init開(kāi)頭的方法必須被定義為實(shí)例方法,其他四個(gè)開(kāi)頭的方法既可以是類(lèi)方法窍箍,也可以是實(shí)例方法串纺。切記嚴(yán)守內(nèi)存管理相關(guān)的函數(shù)命名規(guī)則。不嚴(yán)格遵守可能會(huì)造成無(wú)法釋放椰棘,或誤釋放纺棺。
ARC基本注意事項(xiàng):
禁止調(diào)用引用計(jì)數(shù)相關(guān)的方法(retain,release邪狞,autorelease祷蝌,retainCount)。
禁止使用NSAutoreleasePool帆卓,而是使用新語(yǔ)法@autoreleasepool來(lái)管理自動(dòng)釋放池
切記嚴(yán)守內(nèi)存管理相關(guān)的函數(shù)命名規(guī)則
不用在dealloc中釋放實(shí)例變量(但可以釋放資源)巨朦,也不需要調(diào)用[ super ?dealloc?];
編譯代碼時(shí)使用clang編譯器,并加上編譯選項(xiàng)-fobjc-arc
循環(huán)引用和弱引用:
像這種兩個(gè)對(duì)象互相引用剑令,或者像A持有B糊啡、B持有C、C持有A這樣多個(gè)對(duì)象的引用關(guān)系并
成了環(huán)的現(xiàn)象尚洽,叫作循環(huán)引用或循環(huán)保持(retain cyele)。循環(huán)引用會(huì)造成內(nèi)存泄漏靶累,只有打破循環(huán)
引用關(guān)系才能夠釋放內(nèi)存腺毫。
為了避免循環(huán)引用的出現(xiàn),我們還需要另外一種類(lèi)型的變量挣柬,這種變量能夠引用對(duì)象潮酒,但不會(huì)成為對(duì)象的所有者,不影響對(duì)象本身的回收邪蛔,所以ARC引入了弱引用的概念急黎。弱引用是通過(guò)存儲(chǔ)一個(gè)指向?qū)ο蟮闹羔槃?chuàng)建的,且不保留對(duì)象。OC中用
_ _weak修飾符來(lái)定義弱引用勃教。
通常聲明的未加_ _weak修飾符的變量都是強(qiáng)引用(strong reference)類(lèi)型的變量淤击,聲明時(shí)也可以通過(guò)加上_ _strong修飾符來(lái)明示變量是強(qiáng)引用類(lèi)型。函數(shù)和方法的參數(shù)也是強(qiáng)引用類(lèi)型故源。聲明變量的時(shí)候污抬,_ _weak或者_(dá) _strong可以出現(xiàn)在聲明中的任意位置,如下面的例子所示绳军。但有一點(diǎn)要注意的是印机,最后一個(gè)聲明的變量f前不可省略_ _weak。
_ _weak Nsobject *a,*b门驾;
NSObject _ _weak *c,*d射赛;
NSObject * _ _weak e, * _ _weak f;
這種用于修飾指針類(lèi)型變量的修飾符被叫作生命周期修飾符或所有權(quán)修飾符。生命周期修飾符一共有四種:
_ _weak
_ _strong
_ _autoreleasing
_ _unsafe_unretained
弱引用會(huì)在其指向的實(shí)例對(duì)象被釋放后自動(dòng)變成nil奶是,所以不用擔(dān)心變成野指針楣责。雖然弱引用好處有很多,但也不能濫用诫隅。下面這行代碼是一個(gè)極端的例子:
_ _weakPeople*w = [[Peoplealloc]initWithName:"chenzhen”];
因?yàn)橘x值了一個(gè)弱引用截亦,所以生成的對(duì)象會(huì)被立刻釋放阐枣。
對(duì)象之間引用關(guān)系的基本原則:
對(duì)象圖中的環(huán)路就是循環(huán)引用產(chǎn)生的原因,使用ARC的時(shí)候應(yīng)該盡量保證對(duì)象之間的關(guān)系呈樹(shù)形結(jié)構(gòu)避免同一個(gè)對(duì)象同時(shí)被兩處引用。這樣的話粹庞,當(dāng)一個(gè)對(duì)象被釋放的時(shí)候,這個(gè)對(duì)象的引用對(duì)象也會(huì)被自然的釋放譬巫。
使用ARC編程時(shí)其他一些注意事項(xiàng):
使用ARC的時(shí)候捣鲸,如果既不想保持賦值的對(duì)象,也不想賦值的對(duì)象在釋放后被自動(dòng)的設(shè)為nil甸箱,可以使用生命周期修飾符:
_ _unsafe_unretained育叁。該修飾符所修飾的變量稱(chēng)為非nil化的弱指針,也就是說(shuō)芍殖,如果所指向的內(nèi)存區(qū)域被釋放了豪嗽,這個(gè)指針就是一個(gè)野指針了。
還有一點(diǎn)要注意的是豌骏,手動(dòng)內(nèi)存管理時(shí)也有可能在賦值時(shí)沒(méi)對(duì)變量進(jìn)行retain操作龟梦,這就相當(dāng)于在ARC中用_ _weak、 _ _unsafe_unretained修飾了變量的行為窃躲。因此计贰,將這種代碼遷移到ARC環(huán)境的時(shí)候一定要小心,需要重新考慮所有權(quán)方面的問(wèn)題蒂窒。
有一些類(lèi)的實(shí)例不能使用自動(dòng)化nil的弱引用躁倒,但可以使用 _ _unsafe_unretained來(lái)代替_ _ weak荞怒。
在使用ARC的程序中,id類(lèi)型和void*類(lèi)型之間不能進(jìn)行轉(zhuǎn)型秧秉。就算加了 _ _unsafe_unretained修飾符褐桌,轉(zhuǎn)型操作在編譯時(shí)也會(huì)報(bào)錯(cuò)。這是因?yàn)楦U辏琲OS世界中主要有兩種對(duì)象:Objective-C對(duì)象和Core Foundation對(duì)象撩嚼。其中,Core Foundation類(lèi)型的對(duì)象不在ARC的管理范疇內(nèi)挖帘。因此完丽,當(dāng)轉(zhuǎn)換這兩種類(lèi)型(一種有ARC管理,一種沒(méi)有ARC管理)時(shí)拇舀,就需要告訴編譯器怎樣處理對(duì)象的所有權(quán)逻族。為了解決這一問(wèn)題,可以使用_ _bridge修飾符來(lái)實(shí)現(xiàn)id類(lèi)型與void*類(lèi)型的相互轉(zhuǎn)換(更多詳細(xì)內(nèi)
容請(qǐng)參考附錄B)骄崩。
有一種減少野指針出現(xiàn)的方法是聘鳞,當(dāng)不再使用傳入的對(duì)象時(shí),將其賦值為nil
通過(guò)函數(shù)的參數(shù)返回結(jié)果對(duì)象:
當(dāng)一個(gè)函數(shù)或方法有多個(gè)返回值時(shí)要拂,我們可以通過(guò)函數(shù)或方法的參數(shù)傳入一個(gè)指針抠璃,將返回值寫(xiě)入指針?biāo)赶虻目臻g。C語(yǔ)言中把這種方法叫作按引用傳遞(pass-by-reference ).Objective-C 的ARC中也有類(lèi)似的方法脱惰,但采用了和C語(yǔ)言不同的實(shí)現(xiàn)方式搏嗡,叫作寫(xiě)回傳(pass-by-writeback),寫(xiě)回傳經(jīng)常被用于當(dāng)一個(gè)方法在處理過(guò)程中出現(xiàn)錯(cuò)誤時(shí)拉一,通過(guò)指向NSError的二重指針?lè)祷劐e(cuò)誤的種類(lèi)和原因(關(guān)于錯(cuò)誤處理的詳細(xì)內(nèi)容采盒,請(qǐng)參考18.5節(jié))。下面這個(gè)聲明是NSString的一個(gè)初始化函數(shù)蔚润,它會(huì)從指定的文件讀入內(nèi)容來(lái)完成NSString的初始化磅氨。如果初始化失敗,則通過(guò)error返回錯(cuò)誤的種類(lèi)和原因嫡纠。error是二重指針類(lèi)型烦租,*error指向的是NSError*類(lèi)型的變量。
只可以把nil或臨時(shí)變量的指針用于寫(xiě)回傳除盏,不可以把靜態(tài)變量的指針叉橱,數(shù)組首地址的指針或內(nèi)部變量的指針用于寫(xiě)回傳。
將二重指針用于方法的參數(shù)時(shí)痴颊,可以給二重指針加上out修飾符赏迟。out修飾符原本時(shí)被用于提高調(diào)用分布式對(duì)象的效率的屡贺,使用寫(xiě)回傳的情況下蠢棱,方法調(diào)用和方法返回的時(shí)候都會(huì)發(fā)生值傳遞锌杀,而通過(guò)使用out修飾符,就可以使函數(shù)只在返回的時(shí)候發(fā)生值傳遞泻仙,從而省略調(diào)用函數(shù)時(shí)的值傳遞糕再。
c語(yǔ)言數(shù)組保存objective-c對(duì)象:
ARC有效的程序中可以用c語(yǔ)言數(shù)組保存oc對(duì)象。
動(dòng)態(tài)分配內(nèi)存可以使用calloc函數(shù)玉转。ARC中不可以使用memset()突想,bzero(),memcpy()等操作內(nèi)存的函數(shù)究抓,因?yàn)锳RC會(huì)監(jiān)視這些函數(shù)的行為猾担,所以使用后就有可能造成內(nèi)存段錯(cuò)誤。
ARC有效的情況下刺下,不可以在c語(yǔ)言結(jié)構(gòu)體(或共用體)中定義oc對(duì)象绑嘹,原因是編譯器不能自動(dòng)釋放結(jié)構(gòu)體內(nèi)的oc對(duì)象。如果非要用的話橘茉,可以使用_ _unsafe_unretained來(lái)修飾結(jié)構(gòu)體中的oc變量工腋。這樣一來(lái)編譯器就不會(huì)管理這個(gè)內(nèi)存的變量,完全需要手動(dòng)管理內(nèi)存畅卓。(引用計(jì)數(shù)也不可用)擅腰。