多線程與內(nèi)存管理讀書摘錄(引用計(jì)數(shù)ARC篇)

一.內(nèi)存管理的思考方式

1.引用計(jì)數(shù)的思考方式:

  • 自己生成的對(duì)象,自己持有
  • 非自己生成的對(duì)象,自己也能持有
  • 不再需要自己持有的對(duì)象時(shí)釋放
  • 非自己持有的對(duì)象無(wú)法釋放
對(duì)象操作與Objective-C 方法的對(duì)應(yīng)
對(duì)象操作 Objcetive-C 方法
生成并持有對(duì)象 alloc/new/copy/mutableCopy 等方法
持有對(duì)象 retain
釋放對(duì)象 release
廢棄對(duì)象 dealloc

2.alloc/retain.release/dealloc 實(shí)現(xiàn)

GNUstep是Cocoa框架的互換框架。理解了GNUstep源代碼也就相當(dāng)于理解了蘋果的Cocoa實(shí)現(xiàn)。

a.通過(guò)allocWithZone:類方法調(diào)用NSAllocateObject函數(shù)分配了對(duì)象。

b.NSAllocateObject函數(shù)通過(guò)調(diào)用NSZoneMalloc函數(shù)來(lái)分配存放對(duì)象所需的內(nèi)存空間,之后將改內(nèi)存空間置0,最后返回作為對(duì)象而使用的指針。

c.NSZone:為防止內(nèi)存碎片化而引入的結(jié)構(gòu)

總結(jié):GNUstep中alloc/retain/release/dealloc 的實(shí)現(xiàn)
  • 在Objective-C的對(duì)象中存有引用計(jì)數(shù)這一整數(shù)值。
  • 調(diào)用alloc或是retain方法后,引用計(jì)數(shù)值加1
  • 調(diào)用release后,引用計(jì)數(shù)值減1
  • 引用計(jì)數(shù)值為0時(shí),調(diào)用dealloc方法廢棄對(duì)象
GNUstep的實(shí)現(xiàn) 蘋果的實(shí)現(xiàn)
將引用計(jì)數(shù)保存在對(duì)象占用內(nèi)存塊頭部的變量中 保存在引用計(jì)數(shù)表的記錄中
少量代碼即可完成 對(duì)象用內(nèi)存塊的分配無(wú)需考慮內(nèi)存塊頭部
能夠統(tǒng)一管理引用計(jì)數(shù)用內(nèi)存塊與對(duì)象用內(nèi)存塊 引用計(jì)數(shù)表各記錄中存有內(nèi)存快地址,可從各個(gè)記錄追溯到各對(duì)象的內(nèi)存塊

3.autorelease理解

定義:自動(dòng)釋放,當(dāng)對(duì)象實(shí)例超出作用域(相當(dāng)于變量作用域)時(shí),對(duì)象實(shí)例的release實(shí)例方法被調(diào)用。

  • 生成并持有NSAutoreleasePool對(duì)象
  • 調(diào)用已分配對(duì)象的autorelease實(shí)例方法
  • 廢棄NSAutoreleasePool對(duì)象
autorelease實(shí)現(xiàn)
GNUstep的實(shí)現(xiàn) 蘋果的實(shí)現(xiàn)
atuorelease實(shí)例方法的本質(zhì)就是調(diào)用NSAutoreleasePool對(duì)象的addObject方法 通過(guò)objc4庫(kù)的runtime/objc-arr.mm 確認(rèn)autorelease實(shí)現(xiàn) 類似

4.ARC規(guī)則

A.所有權(quán)修飾符
  • __strong 修飾符
  • __weak 修飾符
  • __unsafe _unretained 修飾符
  • __autoreleasing 修飾符
1).__strong 修飾符

__strong 修飾符是id類型和對(duì)象類型默認(rèn)的所有權(quán)修飾符,表示對(duì)對(duì)象的“強(qiáng)引用”。持有強(qiáng)引用的變量在超出其作用域是被廢棄,隨著強(qiáng)引用的失效,引用的對(duì)象會(huì)隨之釋放。通過(guò) _strong 修飾符,不必再次鍵入retain或者release,完美地滿足了 “引用計(jì)數(shù)式內(nèi)存管理的思考方式”。

2).__weak 修飾符

循環(huán)引用容易發(fā)生內(nèi)存泄露。所謂內(nèi)存泄漏就是應(yīng)當(dāng)廢棄的對(duì)象在超出其生存周期后繼續(xù)存在

__weak 修飾符的一個(gè)優(yōu)點(diǎn):在持有某對(duì)象的弱引用時(shí),若該對(duì)象被廢棄,則此弱引用將自動(dòng)失效且處于nil被賦值的狀態(tài)(空弱引用)。

3).__unsafe _unretained 修飾符

__weak 修飾符只能用于iOS5 以上以及OS X Lion 以上版本的應(yīng)用程序,之下版本使用了 __unsafe _unretained 修飾符。

__unsafe _unretained 是不安全的所有權(quán)修飾符,附有 __unsafe _unretained 修飾符的變量不屬于編譯器的內(nèi)存管理對(duì)象。

4).__autoreleasing 修飾符

ARC有效時(shí)

@autoreleasepool{
   id __autoreleasing obj = [[NSObject alloc] init];
}

指定“@autoreleasepool塊”來(lái)替代“NSAutoreleasePool類對(duì)象生成、持有以及廢棄”這一范圍。

id的指針或?qū)ο蟮闹羔槙?huì)默認(rèn)附加上__autoreleasing 修飾符

- (BOOL) performOperationWithError:(NSError **) error;
等同于
- (BOOL) performOperationWithError:(NSError * __autoreleaasing *) error;

賦值給對(duì)象指針是,所有權(quán)修飾符必須一致

///正確寫法
NSError *error = nil;
NSError *__strong *pError = &error;

了解@autoreleasepool

NSRunLoop等實(shí)現(xiàn)不論ARC有效還是無(wú)效,均能夠隨時(shí)釋放注冊(cè)到autoreleasepool中的對(duì)象

autoreleasepool 范圍以塊級(jí)源代碼表示,提高了程序的可讀性,所以今后在ARC無(wú)效時(shí)也推薦使用@autoreleasepool塊。


B.具體的ARC規(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 * ”
1).不能使用retain/release/retainCount/autorelease

設(shè)置ARC有效時(shí),無(wú)需(禁止)再次鍵入retain或release代碼

即使ARC被設(shè)置為無(wú)效時(shí),該源代碼也完全不符合引用計(jì)數(shù)式內(nèi)存管理的思考方式。

2).不能使用NSAllocateObject/NSDeallocateObject
3).須遵守內(nèi)存管理的方法命名規(guī)則
  • alloc
  • new
  • copy
  • mutableCopy
  • init(ARC有效時(shí),更嚴(yán)格,改方法必須是實(shí)例方法,并且必須要返回對(duì)象,返回的對(duì)象應(yīng)為id類型或該方法聲明類的對(duì)象類型,抑或是該類的超類或子類型)
雖然以init開(kāi)始的方法但并不包含在上述命名規(guī)則里
 - (void) initialize;
4).不要顯示調(diào)用dealloc

deallo方法在大多數(shù)情況下適用于刪除已注冊(cè)的代理或觀察者對(duì)象

5).使用@autoreleasepool塊替代NSAutoreleasePool
6).不能使用區(qū)域(NSZone)

區(qū)域在現(xiàn)在的運(yùn)行時(shí)系統(tǒng)已經(jīng)單純地被忽略

7).對(duì)象型變量不能作為C語(yǔ)言結(jié)構(gòu)體(struct/union)的成員
struct data{
   NSMutableArray *array;
};
error: ARC forbids Objective-C objs in structs or unions
NSMutableArray *array;

要把對(duì)象型變量加入到機(jī)構(gòu)提成員中時(shí),可強(qiáng)制轉(zhuǎn)換為 void * 或是附加 __unsafe _unretained 修飾符

struct Data{
      NSMutableArray __unsafe_unretained *array;
};
附有 __unsafe_unretained 修飾符的變量不屬于編譯器的內(nèi)存管理對(duì)象
8).顯示轉(zhuǎn)換“id”和“void * ”

__bridge 轉(zhuǎn)換

  • 安全性與賦值低,容易因?yàn)閼掖怪羔樁鴮?dǎo)致程序奔潰
  • 還有__bridge_retained 與 __bridge_transfer兩種轉(zhuǎn)換
  • 在Objective-C對(duì)象與Core Foundation 對(duì)象之間相互變換
屬性
屬性聲明的屬性 所有權(quán)修飾符
assign __unsafe _unretained 修飾符
copy(不是簡(jiǎn)單的賦值,它賦值的是通過(guò)NSCopying接口的copyWithZone:方法賦值源所生成的對(duì)象 __strong 修飾符 (賦值的是被復(fù)制的對(duì)象)
retain __strong 修飾符
strong __strong 修飾符
unsafe_unretained __unsafe _unretained
weak __weak 修飾符
數(shù)組

在分配內(nèi)存時(shí)推薦使用calloc函數(shù)

C. ARC的實(shí)現(xiàn)

ARC由以下工具、庫(kù)來(lái)說(shuō)實(shí)現(xiàn)

  • clang (LLVM編譯器)3.0以上
  • objc4 Objective-C 運(yùn)行時(shí)庫(kù) 493.9以上

__strong修飾符

objc_retainAutoreleasedReturnValue objc_autoreleaseReturnValue
alloc/new/copy/mutableCopy之外類方法等返回對(duì)象的實(shí)現(xiàn)上 alloc/new/copy/mutableCopy

__weak修飾符 [重點(diǎn)]

  • 若附有__weak 修飾符的變量所引用的對(duì)象被廢棄,則將nil賦值給該變量
  • 使用附有__weak 修飾符的變量,即是使用注冊(cè)到autoreleasepool中的對(duì)象
 {
  id __weak obj1 = obj;
 }

 /* 編譯器模擬代碼*/
 id obj1;
 objc_initWeak(&obj1,obj);
 objc_destoryWeak(&obj1);
 *******
 obj1 = 0;
 objc_storeWeak(&obj1,obj);
  *******

objc_storeWeak 函數(shù)把第二參數(shù)的賦值對(duì)象的地址作為鍵值,將第一參數(shù)的附有__weak修飾符的變量的地址注冊(cè)到weak表中。如果第二參數(shù)為 0,則把變量的地址從weak表中刪除。weak表與引用計(jì)數(shù)表相同,作為散列表被實(shí)現(xiàn)。

釋放對(duì)象,程序的動(dòng)作:

  • objc_release
  • 引用計(jì)數(shù)為0,執(zhí)行dealloc
  • _ objc _rootDealloc
  • object_dispose
  • objc_destructInstance
  • objc_clear_deallocating

objc _clear _deallocating的函數(shù)動(dòng)作

1.從weak表中獲取廢棄對(duì)象的地址為鍵值的記錄
2.將包含在記錄中的所有附有__weak修飾符變量的地址,賦值為nil
3.從weak表中刪除該記錄
4.從引用計(jì)數(shù)表中刪除廢棄對(duì)象的地址為鍵值的記錄

只在需要避免循環(huán)引用時(shí)使用__weak修飾符,否則會(huì)消耗相應(yīng)的CPU資源

__autoreleasing修飾符

引用計(jì)數(shù)

{
   id __strong obj = [[NSObject alloc] init];
   NSLog(@"retain count = %d",_objc_rootRetainCount(obj));
}

實(shí)際上并不能夠完全信任_objc_rootRetainCount函數(shù)取得的數(shù)值。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容