該系列文章系個人讀書筆記及總結性内容,任何組織和個人不得轉載進行商業活動!
本節接着介紹記憶體管理相關的内容;
第4章 記憶體管理
4.4 使用自動引用計數
在使用MRR記憶體管理方式時,你會親自管理程式中對象回收工作;
好處是可以精細地控制記憶體的使用情況;
壞處是大幅度增加了開發人員的負擔;
是以,手動引用計數更容易出錯,會導緻程式洩漏記憶體或崩潰;最好能使用工具完成這類基本任務;
自動引用計數(ARC):
是一種功能強大的記憶體管理工具,是這類任務自動化;
ARC使用引用計數的模式與MRR使用的相同,但是由編譯器管理回收對象的工作;
具體的:
在編譯程式時,編譯器會分析源代碼,确定以動态方式建立的對象的回收需求,然後在已編譯代碼的必要位置自動插入retain和release消息;
APR還可以(潛在地)提升應用的性能和消除記憶體管理錯誤(如錯誤釋放仍在使用的對象、保留不再使用的對象);
此外,和垃圾回收機制相比,ARC更可靠(保留和釋放語句是在編譯時插入的),并且不會為實作垃圾回收機制而在程式執行過程中引入暫停操作;
ARC可以為OC對象和塊提供自動記憶體管理功能;
注意,ARC無法自動處理循環引用;
為此,OC提供了弱引用功能,在必要時通過該功能你可以手動解消循環引用;
4.4.1 使用ARC的規則和約定
與MRR相比,ARC可大幅度簡化記憶體管理工作;
使用ARC時應遵守規則:
1)不能手動編寫發送retain、retainCount、release、autorelease和dealloc消息的代碼;
ARC不允許手動方式控制對象的回收工作;
如果需要管理執行個體變量之外的資源,可以手動編寫dealloc方法(比如登出通知);
ARC在您沒有在類中編寫dealloc方法的情況下,會自動建立該方法,釋放其中的對象,并調用父類的dealloc方法;
在類中編寫dealloc方法的情況下,是不能再調用[super dealloc]的;
2)不能直接進行id和(void *)類型的互轉;
ARC隻能管理OC對象和塊,是以編譯器隻能處理能夠識别出其類型的對象;
因為(void *)類型的指針是能夠轉換為任何指針類型(包括OC中沒有的指針類型)的通用指針;是以必須設定這個限制;
當使用Foundation架構對象和Core Foundation對象(一種提供C語言API的Apple軟體庫)互相轉換類型時,此種情況較常見,會有相應的API供在ARC和非ARC環境之間進行變量所有權的轉移;
3)需要使用自動釋放池代碼塊執行由ARC管理的自動釋放操作;
4)不能調用Foundation架構函數NSAllocateObject和NSDeallocateObject;
這兩個函數提供了在指定記憶體區域為對象配置設定和釋放記憶體的功能;因為OC不再支援記憶體區,是以無法使用它們;
5)無法使用C結構中的對象指針;
ARC不能為動态配置設定的C結構執行記憶體管理操作,因為編譯器無法判斷何時應插入必需的retain和release消息;
6)不能使用記憶體區(NSZone);如前所述,OC不再支援記憶體區;
7)為了與非ARC代碼協作,不能建立以copy開頭的方法和自動聲明屬性(除非明确定義了其他讀取器);
8)預設情況下,ARC并非異常安全的;
無法釋放異常失效的__strong變量;
無法在完整表達式抛出異常時,将位于該完整表達式末尾的對象釋放;
使用編譯器選項-fobjc-arc-exceptions可以啟動處理ARC代碼異常的功能;除非編譯器解決了該異常,否則當弱引用異常失效時,ARC肯定會釋放弱引用變量;
上邊都是一些規則,有的比較難懂,在不斷學習和成長中,慢慢體會吧;
4.4.2 ARC的聲明周期限定符
OC提供了一系列專用的ARC限定符,使用它們可以聲明正常變量和屬性的生命周期;
應用于正常變量的限定符:
1)__strong:
表明使用任何alloc/init消息建立的對象都會在其作用範圍内被保留;
“作用範圍”通常是指變量聲明所在花括号對的内部(如方法、循環、條件語句塊);
這是正常變量的預設設定;
2)__weak:
表明對象随時都可以被釋放;
隻有當對象擁有其他強引用時,該标記才會有用處;(因為weak是弱引用)
對象被釋放之後,待__weak限定符的變量會被設定為nil;
3)__unsafe_unretained:
與__weak限定符類似,但是在對象被釋放後,指針不會被置為nil,而是處于懸空狀态(不再指向合法對象);
是以是不安全的 也不會獲得對象的所有權的;
4)__autoreleasing:
不要将該限定符與調用對象中的autorelease方法搞混了,這個限定符用于通過引用傳遞對象;
使用ARC生命周期限定符聲明OC的對象變量時,文法如下:
類名 * 限定符 變量名;
如果沒有設定限定符,系統會使用預設值__strong;
應用于屬性的ARC生命周期限定符:
1)strong:
等同于retain特性,擷取對象所有權;
2)weak:
類似于assign特性,無數次被問到過assign和weak的差別,兩者都是簡單指派操作,差別在于,weak特性限定的,如果引用對象被釋放了,其執行個體變量會被設定為nil;
ARC中,strong是對象型屬性預設的所有權特性;
(後續會詳細介紹一下屬性的特性)
4.4.3 使用ARC
ARC中類的init方法(無對象變量指派)和MRR中的一樣;
但dealloc方法不同,ARC中的不調用父類的dealloc方法,因為這是由ARC自動執行的操作;
給出一個例子:
MRR:
-initWithName:(NSString *)name{
if(self = [super init]){
_name = name;
[_name retain];
}
return self;
}
-(void)dealloc{
[_name release];
[super dealloc];
}
ARC:
-initWithName:(NSString *)name{
if(self = [super init]){
_name = name;
}
return self;
}
-(void)dealloc{
}
通過ARC記憶體管理方式建立的對象,不再使用時(比如置為nil,或作用範圍結束),ARC會自動釋放它們;
ARC還會管理必需的依存對象,避免出現正在使用對象的情況;
4.4.4 避免循環引用
OC的引用計數模型是通過擷取對象所有權(通過retain消息),以及再不使用對象後釋放對象的所有權(通過release)實作的;
ARC自動化了該過程;
它會根據需要在代碼中自動插入retain/release/autorelease消息;
然而,如果兩個對象直接或間接互相引用,就會導緻循環引用問題;
比如,類A的對象a擁有一個類B的執行個體,如果類B的這個執行個體對象也擁有類A的對象執行個體,就會在這兩個對象之間造成循環引用;
兩個對象将永遠不會被釋放;
解決方式是使用弱引用:
弱引用是一種非所有權引用,被弱引用的對象不屬于引用它的對象,進而消除循環引用;
OC約定:
父對象強引用其所有子對象;
子對象弱引用其父對象;
是以上述的問題可以這樣修改:
@Interface A:NSObject{
@public B * b;
}
@Interface B:NSObject{
@public A * __weak a;
}
當B類對象被釋放時,變量a也會被置為nil;避免了循環引用;
4.5 小結
本章介紹了:
OC中的記憶體管理,包括:
OC記憶體模型;
為OC程式配置設定和釋放記憶體的方式;
以及兩種OC記憶體管理機制的用法;
要點:
1)在運作時,OC程式建立的對象(通過NSObject類的alloc方法)會以動态方式存儲在預先配置設定的記憶體區域中,這片記憶體區域稱為堆記憶體;
以動态方式建立對象意味着需要管理記憶體,因為在堆記憶體中建立的對象會一直使用該區域中的記憶體;
不進行記憶體管理或者采用錯誤的記憶體管理方式,通常會導緻記憶體洩漏和懸挂指針問題;
2)OC的記憶體管理是使用引用計數實作的;
該技術通過對象的唯一引用判斷對象是否正在被使用;
如果某個對象的引用計數變為0,那麼就會被認為不再有用;運作時系統會釋放它占用的記憶體;
3)OC開發環境有兩種記憶體管理機制:手動管理(MRR)和自動引用計數(ARC);
4)在使用MRR記憶體管理方式時,需要編寫确切的代碼,管理對象的生命周期、擷取對象(你建立的或需要使用的)所有權和釋放對象(不再需要的)所有權;
5)ARC使用的引用計數模型與MRR使用的引用計數模型相同,不同的是ARC通過編譯器自動管理對象的生命周期;
在編譯程式時,編譯器會分析源代碼,确定以動态方式建立的對象的生命周期,然後在已編譯的代碼中自動插入必需的retain和release消息;
6)ARC中增加了新的對象生命周期限定符,使用這些限定符可以确切地聲明對象變量和屬性的生命周期,還可以實作弱引用,避免出現循環引用;
精巧的OC程式僅會占用它所必需的記憶體,不會使記憶體洩漏和嘗試通路已經失效的對象;
接下來做好準備,迎接下一章——預處理器。
該系列文章系個人讀書筆記及總結性内容,任何組織和個人不得轉載進行商業活動!
上一節介紹了消息傳遞和消息轉發,接下來我們看看記憶體管理相關的内容;