前言
前幾天,《Objective-C高級編程》自動引用計數 閱讀筆記 一文基本已經涉及到內存管理的各方面知識點,但是,在閱讀《精通Objective-C》 第4章 內存管理的過程中,仍然可以學到不少東西,不僅僅是作者對于內存管理的另外一種理解方式,也從側面反映出內存管理中不可或缺的重要的某些特性和要點。
Demo
Pro_Objective-C_ARC_Demo 提取碼: i4p5
第4章 內存管理
問題1 對象引用和對象所有權
- 通過指向Objective-C對象內存地址的變量(即*** 指針*** ),以*** 間接方式*** 訪問Objective-C對象。在聲明時,指針的名稱中會帶有*前綴。
- 指針和間接訪問方式可以應用于*** 所有Objective-C數據類型*** ,其中包括Objective-C的基本數據類型和C語言數據類型;然而,*** 對象指針*** 專門用于*** Objective-C對象的交互操作*** 。
- 對象指針實現了Objective-C對象的訪問功能,但是它們本身*** 不能管理所有權*** 。(因此,才需要內存管理機制)
問題2 內存管理基本原則(思考方式)
- 自己生成的對象,自己持有(alloc/new/copy/mutableCopy方法群)
- 非自己生成的對象,自己也能持有(使用retain方法)
- 不再需要自己持有的對象時釋放(可以使用release方法和autorelease方法)
- 非自己持有的對象不能釋放(會導致潛在的懸掛指針問題)
*** 內存管理思考方式的講解可以參考《Objective-C高級編程》自動引用計數相關章節。***
問題3 使用自動引用計數(ARC)
- ARC使用的引用計數模型與MRR使用的相同,但是由*** 編譯器*** 管理回收對象(對象的生命周期)的工作。
- ARC可以(潛在地)提升應用的性能和消除內存管理錯誤(如錯誤釋放正在使用的對象,保留不再使用的對象)。
- 與垃圾回收技術相比,ARC更可靠(保留和釋放語句是在編譯時插入的),并且不會為實現垃圾回收機制而在程序執行過程中引入暫停操作。
- ARC可以Objective-C對象和塊提供*** 自動內存管理*** 。
- 由于ARC無法自動處理循環引用,Objective-C提供了*** 弱引用*** 功能,用于手動解除*** 循環引用*** 。
問題4 使用ARC的規則和約定
- 不能手動編寫發送*** retain、retainCount、release、autorelease和dealloc消息*** 的代碼,但是,如果需要管理實例變量之外的資源,可以手動編寫dealloc方法。
- 不能直接進行*** id和(void *)類型的互轉*** 。ARC只能管理Objective-C對象和塊,因此編譯器只能夠處理Objective-C對象和塊,而(void *)類型的指針是能夠轉換為任何指針類型(包括Objective-C中沒有的指針類型)的通用指針,所以必須做此限制。
- 在autoreleasepool塊結束時,*** 自動釋放由ARC管理的對象***
- 不能調用Foundation框架中的*** NSAllocateObject函數*** 和*** NSDeallocateObject函數*** (因為這兩個函數提供在指定內存區域中為對象分配和釋放內存的功能,而Objective-C不再支持內存區,所以無法使用它們)。
- 不能使用*** C結構中的對象指針*** 。
- 不能使用*** 內存區(NSZone)*** 。
- 為了與非ARC代碼協作,不能創建*** 以copy開頭的方法*** 和*** 自動聲明屬性*** 。
- 默認情況下,ARC并非異常安全的:它無法釋放異常失效的__strong變量,也無法在完整表達式拋出異常時,將位于該完整表達式末尾的對象釋放。使用編譯器選項*** -fobjc-arc-exceptions*** 可以啟動處理ARC代碼異常的功能。除非編譯器解決了該異常,否則當弱引用異常失效是,ARC肯定會釋放弱引用變量。
問題5 ARC的生命周期限定符(所有權修飾符)
*** 應用于常規變量的限定符 ***
- __strong: *** 默認的限定符設置*** 。表明變量持有alloc/new/copy/mutableCopy方法群創建的對象的強引用,變量會在其作用域里被保留)。
- __weak: *** 用于消除循環引用*** 。表明變量持有該對象的弱引用(即變量不持有該對象),對象隨時可以被釋放。當對象被釋放后,附有__weak限定符的變量會被設置為nil。
- __unsafe_unretained: 與__weak限定符類似。但是,在對象被釋放后,指針(即變量)不會被設置為nil,而是會處于*** 懸掛狀態*** (不再指向合法對象)。
- __autoreleasing:*** 用于通過引用傳遞對象*** 。 alloc/new/copy/mutableCopy方法群以外的方法創建的對象,經編譯器檢查,會自動將返回值的對象注冊到autoreleasepool中。
*** 應用于屬性的限定符***
- strong: 等同于retain特性。
- weak: 類似于assign特性,但是如果引用對象被釋放了,其實例變量會被設置為nil。
本章要點
介紹了Objective-C中的內存管理,其中包括Objective-C內存模型、為Objective-C程序分配和釋放內存的方式,以及兩種Objective-C內存管理機制的用法,要點有:
- 在運行時,Objective-C程序創建的對象會以動態方式存儲在預先分配的內存區域中,這片內存區域稱為*** 堆內存 *** 。以動態方式創建對象意味著*** 需要管理內存*** ,因為在堆內存中創建的對象會一直使用該區域中的內存。不進行內存管理或者采用錯誤的內存管理方式,通常會導致*** 內存泄露*** 和*** 懸掛指針*** 問題。
- Objective-C的內存管理是使用*** 引用計數*** 實現的,該技術通過對象的唯一引用判斷對象是否正在被使用。如果某個對象的引用計數為0,那么就會被視為不再有用,運行時系統會釋放它占用的內存。
- 蘋果公司的Objective-C開發環境提供兩種內存管理機制:*** 手動管理(MRR)*** 和*** 自動引用計數(ARC)*** 。
- 在使用MRR內存管理方式時,需要編寫確切的代碼,*** 管理對象的生命周期、獲取對象(自己創建的或者需要持有的)所有權和釋放對象(不再需要的)所有權*** 。
- ARC使用的引用計數模型與MRR使用的引用計數模型相同,但是它通過*** 編譯器*** 自動管理對象的生命周期。在編譯程序時,編譯器會分析源代碼,確切以動態方式創建的對象生命周期,然后在已編譯代碼中自動插入必需的*** retain*** 和*** release*** 消息。
- ARC中增加了新的對象生命周期限定符(也稱所有權修飾符),使用這些限定符可以確切地聲明對象變量和屬性的生命周期,還可以實現弱引用功能(__weak修飾符),避免出現循環引用。
- ARC能夠以工程為單位應用,也能以文件為單位應用,因此ARC代碼可以與非ARC代碼共存。
第6章 專家級技巧:使用ARC
問題1 Objective-C直接橋接數據類型
Core Foundation數據類型 | Foundation框架數據類型 |
---|---|
CFArrayRef | NSArray |
CFDataRef | NSData |
CFDateRef | NSDate |
CFDictionaryRef | NSDictionary |
CFSetRef | NSSet |
CFStringRef | NSString |
CFMutableArrayRef | NSMutableArray |
CFMutableDataRef | NSMutableData |
CFMutableDictionaryRef | NSMutableDictionary |
CFMutableSetRef | NSMutableSet |
CFMutableStringRef | NSMutableString |
CFNumberRef | NSNumber |
CFReadStreamRef | NSInputStream |
CFWriteSteamRef | NSOutputStream |
問題2 ARC橋接轉換
*** __bridge標記 ***
使用__bridge標記可以在*** 不改變所有權*** 的情況下,將對象從Core Foundation框架數據類型轉換為Foundation框架數據類型(反之亦然)。注意,該標記無法解決*** 內存泄露*** 和*** 懸掛指針*** 問題。
*** __bridge_retained標記 ***
使用__bridge_retained標記可以將Core Foundation框架數據類型對象轉換為Foundation框架數據類型對象,并從ARC接管對象的所有權。*** 手動管理*** 直接橋接數據的生命周期。
*** __bridge_transfer標記 ***
使用__bridge_transfer標記可以將Foundation框架數據類型對象轉換為Core Foundation框架數據類型對象,并且會將對象的所有權交給ARC管理。這樣,由*** ARC管理*** 對象的生命周期。
注意:
轉換標記位于數據類型之前。ARC橋接轉換不僅可以用于直接橋接數據類型,還可以用于*** 訪問未分配給Objective-C對象的內存*** 。
本章要點
深入研究了ARC的內存管理方式,著重介紹了ARC中的對象所有權、塊對象和直接橋接(Toll free bridging),要點有:
- ARC*** 禁止*** 通過手動方式,向對象發送*** release、autorelease、dealloc消息以及與對象有關的其他消息*** 。
當執行下列操作時,程序會放棄對象的所有權:
- *** 重新分配變量(變量執行另外一個對象)***
- *** 將nil賦值給變量***
- *** 釋放對象的擁有者***
- 當變量被更改為指向*** 另外一個對象*** 時,變量*** 原來的對象*** 會失去一個擁有者;在編譯時,ARC會插入向該對象發送一條*** release消息*** 的代碼。
- 當指向對象的變量被設置為*** nil*** 時,該對象會失去一個擁有者;在編譯時,ARC會插入向該對象發送一條*** release消息*** 的代碼。
- 當集合類實例被設置為nil時,集合類實例會被*** 釋放*** ,ARC會自動向該集合類實例中的*** 每一個對象*** 發送一條*** release消息*** 。
- 蘋果所提供的基于C語言的API軟件庫沒有與ARC整合,因此,在程序中以動態方式為這些API分配內存時,必須*** 手動管理內存*** (用到了*** __bridge_retained轉換符*** )。
- ARC禁止在Objective-C對象指針和其他類型指針之間標準轉換,但是,通過多種機制(直接橋接和ARC橋接轉換),可以使用在Objective-C程序中使用基于C語言的API。
- 在使用ARC時,無法直接轉換直接橋接的數據類型,必須使用特殊的ARC橋接轉換標記(*** __bridge、__bridge_retained、__bridge_transfer*** )才能進行轉換。