一.內(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ù)值。