一.內(nèi)存管理的思考方式
1.引用計(jì)數(shù)的思考方式:
- 自己生成的對象丑慎,自己持有
- 非自己生成的對象移剪,自己也能持有
- 不再需要自己持有的對象時釋放
- 非自己持有的對象無法釋放
對象操作與Objective-C 方法的對應(yīng)
對象操作 | Objcetive-C 方法 |
---|---|
生成并持有對象 | alloc/new/copy/mutableCopy 等方法 |
持有對象 | retain |
釋放對象 | release |
廢棄對象 | dealloc |
2.alloc/retain.release/dealloc 實(shí)現(xiàn)
GNUstep是Cocoa框架的互換框架究珊。理解了GNUstep源代碼也就相當(dāng)于理解了蘋果的Cocoa實(shí)現(xiàn)。
a.通過allocWithZone:類方法調(diào)用NSAllocateObject函數(shù)分配了對象纵苛。
b.NSAllocateObject函數(shù)通過調(diào)用NSZoneMalloc函數(shù)來分配存放對象所需的內(nèi)存空間剿涮,之后將改內(nèi)存空間置0,最后返回作為對象而使用的指針攻人。
c.NSZone:為防止內(nèi)存碎片化而引入的結(jié)構(gòu)
總結(jié):GNUstep中alloc/retain/release/dealloc 的實(shí)現(xiàn)
- 在Objective-C的對象中存有引用計(jì)數(shù)這一整數(shù)值取试。
- 調(diào)用alloc或是retain方法后,引用計(jì)數(shù)值加1
- 調(diào)用release后怀吻,引用計(jì)數(shù)值減1
- 引用計(jì)數(shù)值為0時瞬浓,調(diào)用dealloc方法廢棄對象
GNUstep的實(shí)現(xiàn) | 蘋果的實(shí)現(xiàn) |
---|---|
將引用計(jì)數(shù)保存在對象占用內(nèi)存塊頭部的變量中 | 保存在引用計(jì)數(shù)表的記錄中 |
少量代碼即可完成 | 對象用內(nèi)存塊的分配無需考慮內(nèi)存塊頭部 |
能夠統(tǒng)一管理引用計(jì)數(shù)用內(nèi)存塊與對象用內(nèi)存塊 | 引用計(jì)數(shù)表各記錄中存有內(nèi)存快地址,可從各個記錄追溯到各對象的內(nèi)存塊 |
3.autorelease理解
定義:自動釋放蓬坡,當(dāng)對象實(shí)例超出作用域(相當(dāng)于變量作用域)時猿棉,對象實(shí)例的release實(shí)例方法被調(diào)用。
- 生成并持有NSAutoreleasePool對象
- 調(diào)用已分配對象的autorelease實(shí)例方法
- 廢棄NSAutoreleasePool對象
autorelease實(shí)現(xiàn)
GNUstep的實(shí)現(xiàn) | 蘋果的實(shí)現(xiàn) |
---|---|
atuorelease實(shí)例方法的本質(zhì)就是調(diào)用NSAutoreleasePool對象的addObject方法 | 通過objc4庫的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類型和對象類型默認(rèn)的所有權(quán)修飾符屑咳,表示對對象的“強(qiáng)引用”萨赁。持有強(qiáng)引用的變量在超出其作用域是被廢棄,隨著強(qiáng)引用的失效兆龙,引用的對象會隨之釋放杖爽。通過 _strong 修飾符,不必再次鍵入retain或者release,完美地滿足了 “引用計(jì)數(shù)式內(nèi)存管理的思考方式”紫皇。
2).__weak 修飾符
循環(huán)引用容易發(fā)生內(nèi)存泄露慰安。所謂內(nèi)存泄漏就是應(yīng)當(dāng)廢棄的對象在超出其生存周期后繼續(xù)存在
__weak 修飾符的一個優(yōu)點(diǎn):在持有某對象的弱引用時,若該對象被廢棄坝橡,則此弱引用將自動失效且處于nil被賦值的狀態(tài)(空弱引用)泻帮。
3).__unsafe _unretained 修飾符
__weak 修飾符只能用于iOS5 以上以及OS X Lion 以上版本的應(yīng)用程序,之下版本使用了 __unsafe _unretained 修飾符计寇。
__unsafe _unretained 是不安全的所有權(quán)修飾符锣杂,附有 __unsafe _unretained 修飾符的變量不屬于編譯器的內(nèi)存管理對象脂倦。
4).__autoreleasing 修飾符
ARC有效時
@autoreleasepool{
id __autoreleasing obj = [[NSObject alloc] init];
}
指定“@autoreleasepool塊”來替代“NSAutoreleasePool類對象生成、持有以及廢棄”這一范圍元莫。
id的指針或?qū)ο蟮闹羔槙J(rèn)附加上__autoreleasing 修飾符
- (BOOL) performOperationWithError:(NSError **) error;
等同于
- (BOOL) performOperationWithError:(NSError * __autoreleaasing *) error;
賦值給對象指針是赖阻,所有權(quán)修飾符必須一致
///正確寫法
NSError *error = nil;
NSError *__strong *pError = &error;
了解@autoreleasepool
NSRunLoop等實(shí)現(xiàn)不論ARC有效還是無效,均能夠隨時釋放注冊到autoreleasepool中的對象
autoreleasepool 范圍以塊級源代碼表示踱蠢,提高了程序的可讀性火欧,所以今后在ARC無效時也推薦使用@autoreleasepool塊。
B.具體的ARC規(guī)則
- 不能使用retain/release/retainCount/autorelease
- 不能使用NSAllocateObject/NSDeallocateObject
- 須遵守內(nèi)存管理的方法命名規(guī)則
- 不要顯示調(diào)用dealloc
- 使用@autoreleasepool塊替代NSAutoreleasePool
- 不能使用區(qū)域(NSZone)
- 對象型變量不能作為C語言結(jié)構(gòu)體(struct/union)的成員
- 顯示轉(zhuǎn)換“id”和“void * ”
1).不能使用retain/release/retainCount/autorelease
設(shè)置ARC有效時茎截,無需(禁止)再次鍵入retain或release代碼
即使ARC被設(shè)置為無效時苇侵,該源代碼也完全不符合引用計(jì)數(shù)式內(nèi)存管理的思考方式。
2).不能使用NSAllocateObject/NSDeallocateObject
3).須遵守內(nèi)存管理的方法命名規(guī)則
- alloc
- new
- copy
- mutableCopy
- init(ARC有效時企锌,更嚴(yán)格榆浓,改方法必須是實(shí)例方法,并且必須要返回對象撕攒,返回的對象應(yīng)為id類型或該方法聲明類的對象類型陡鹃,抑或是該類的超類或子類型)
雖然以init開始的方法但并不包含在上述命名規(guī)則里
- (void) initialize;
4).不要顯示調(diào)用dealloc
deallo方法在大多數(shù)情況下適用于刪除已注冊的代理或觀察者對象
5).使用@autoreleasepool塊替代NSAutoreleasePool
6).不能使用區(qū)域(NSZone)
區(qū)域在現(xiàn)在的運(yùn)行時系統(tǒng)已經(jīng)單純地被忽略
7).對象型變量不能作為C語言結(jié)構(gòu)體(struct/union)的成員
struct data{
NSMutableArray *array;
};
error: ARC forbids Objective-C objs in structs or unions
NSMutableArray *array;
要把對象型變量加入到機(jī)構(gòu)提成員中時,可強(qiáng)制轉(zhuǎn)換為 void * 或是附加 __unsafe _unretained 修飾符
struct Data{
NSMutableArray __unsafe_unretained *array;
};
附有 __unsafe_unretained 修飾符的變量不屬于編譯器的內(nèi)存管理對象
8).顯示轉(zhuǎn)換“id”和“void * ”
__bridge 轉(zhuǎn)換
- 安全性與賦值低抖坪,容易因?yàn)閼掖怪羔樁鴮?dǎo)致程序奔潰
- 還有__bridge_retained 與 __bridge_transfer兩種轉(zhuǎn)換
- 在Objective-C對象與Core Foundation 對象之間相互變換
屬性
屬性聲明的屬性 | 所有權(quán)修飾符 |
---|---|
assign | __unsafe _unretained 修飾符 |
copy(不是簡單的賦值萍鲸,它賦值的是通過NSCopying接口的copyWithZone:方法賦值源所生成的對象 | __strong 修飾符 (賦值的是被復(fù)制的對象) |
retain | __strong 修飾符 |
strong | __strong 修飾符 |
unsafe_unretained | __unsafe _unretained |
weak | __weak 修飾符 |
數(shù)組
在分配內(nèi)存時推薦使用calloc函數(shù)
C. ARC的實(shí)現(xiàn)
ARC由以下工具、庫來說實(shí)現(xiàn)
- clang (LLVM編譯器)3.0以上
- objc4 Objective-C 運(yùn)行時庫 493.9以上
__strong修飾符
objc_retainAutoreleasedReturnValue | objc_autoreleaseReturnValue |
---|---|
alloc/new/copy/mutableCopy之外類方法等返回對象的實(shí)現(xiàn)上 | alloc/new/copy/mutableCopy |
__weak修飾符 [重點(diǎn)]
- 若附有__weak 修飾符的變量所引用的對象被廢棄擦俐,則將nil賦值給該變量
- 使用附有__weak 修飾符的變量脊阴,即是使用注冊到autoreleasepool中的對象
{
id __weak obj1 = obj;
}
/* 編譯器模擬代碼*/
id obj1;
objc_initWeak(&obj1,obj);
objc_destoryWeak(&obj1);
*******
obj1 = 0;
objc_storeWeak(&obj1,obj);
*******
objc_storeWeak 函數(shù)把第二參數(shù)的賦值對象的地址作為鍵值,將第一參數(shù)的附有__weak修飾符的變量的地址注冊到weak表中蚯瞧。如果第二參數(shù)為 0蹬叭,則把變量的地址從weak表中刪除。weak表與引用計(jì)數(shù)表相同状知,作為散列表被實(shí)現(xiàn)秽五。
釋放對象,程序的動作:
- objc_release
- 引用計(jì)數(shù)為0饥悴,執(zhí)行dealloc
- _ objc _rootDealloc
- object_dispose
- objc_destructInstance
- objc_clear_deallocating
objc _clear _deallocating的函數(shù)動作
1.從weak表中獲取廢棄對象的地址為鍵值的記錄
2.將包含在記錄中的所有附有__weak修飾符變量的地址坦喘,賦值為nil
3.從weak表中刪除該記錄
4.從引用計(jì)數(shù)表中刪除廢棄對象的地址為鍵值的記錄
只在需要避免循環(huán)引用時使用__weak修飾符,否則會消耗相應(yīng)的CPU資源
__autoreleasing修飾符
引用計(jì)數(shù)
{
id __strong obj = [[NSObject alloc] init];
NSLog(@"retain count = %d",_objc_rootRetainCount(obj));
}
實(shí)際上并不能夠完全信任_objc_rootRetainCount函數(shù)取得的數(shù)值西设。