+ alloc + allocWithZone: class_creatInstance calloc
調用alloc方法首先調用allocWithZone:類方法,而後調用class_creatInstance函數,最後調用calloc來分配內存塊。安全
- retainCount __CFDoExternRefOperation CFBasicHashGetCountOfKey
- retain __CFDoExternRefOperation CFBasicHashAddValue
- retainCount __CFDoExternRefOperation CFBasicHashRemoveValue //CFBasicHashRemoveValue 爲0時,-release調用dealloc
各個方法都經過同一個__CFDoExternRefOperation函數,調用一系列名稱類似的函數。而且從函數名看出蘋果採用散列表(引用計數表)來管理引用計數,表鍵值爲內存塊地址的散列值。然而GNUStep將引用計數保存在對象佔用內存塊頭部的變量中(objc_layout這個結構體中)。多線程
內存塊頭部管理引用計數的好處:框架
引用技術表管理引用計數的好處:
1. 對象內存快的分配無需考慮內存塊頭部函數
第二條特徵在調試時很重要,即便出現故障致使對象佔用的內存塊損壞,但只要引用計數表沒有被損壞,就可以確認各個內存塊的地址
NSAutoreleasePool是經過以AutoreleasePoolPage爲結點的雙向鏈表來實現的。AutoreleasePoolPage是一個C++實現的類,類結構如圖:oop
在Cocoa框架中,NSRunloop每次循環過程當中NSAutoreleasePool對象被生成或廢棄。在大量產生autorelease對象時,只要不廢棄NSAutoreleasePool那麼生成的對象就不能被釋放,在此狀況下有時會產生內存不足的現象,所以有必要適當的生成,持有和廢棄NSAutoreleasePool。一般在使用Objective-C,不管調用哪個對象的autorelease/retain方法,實現上都是調用NSObject類的autorelease/retain實例方法,可是對於NSAutoreleasePool類,autorelease/retain實例方法已被重寫,所以運行時會出錯(exception)。autorelease實際上把對象的釋放時機交給NSAutoreleasePool管理,使用方法以下:spa
生成並持有NSAutoreleasePool對象。線程
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // 等同於 objc_autoreleasePoolPush()
調用已分配對象的autorelease實例方法。指針
id obj = [[NSObject alloc] init]; [obj autorelease]; // 等同於 objc_autorelease()obj
廢棄NSAutoreleasPool對象(自動調用分配對象的release)。調試
[pool drain]; // 等同於 objc_autoreleasePoolPop(pool)
ARC(Automatic Reference Counting)是編譯階段自動作了retain/release,原先須要手動添加處理引用計數的代碼能夠自動地由編譯器完成,但實際上只有編譯器是沒法徹底勝任的,在此基礎上還須要Objective-C運行時庫協助。同一程序中按文件單位能夠選擇ARC有效和無效。Core Foundation中的malloc()或者free()等,仍是須要本身手動進行內存管理。ARC規則以下code
對象型變量不能做爲C語言結構體的成員。
struct Data { NSMutableArray *array; /* error: ARC forbids Objective-C objs in structs or unions NSMutableArray *array */ } /* 要把對象型白能量加入到結構體成員中時,可強制轉換爲void *(見下一條規則)或是附加「__unsafe_unreatained」修飾符。可是附有「__unsafe_unreatained」修飾的變量不屬於編譯器的內存管理對象,可能形成內存泄露或者崩潰。*/ struct Data { NSMutableArray __unsafe_unreatained *array; / }
顯式轉換「id」 和 「void」。
在MRC下"id"和"void *"能夠強制轉換,但在ARC下編譯器報錯,代碼以下:
id obj = [[NSObject alloc] init]; void *p = obj; // ARC編譯報錯 id o = p; // ARC編譯報錯 [o release];
ARC狀況下要用 "bridge轉換",可是ARC不推薦此用法,這種轉換常常用在Objective-C和Core Foundation對象之間的相互變換。
__bridge_retained轉換:可以使目標變量也持有賦值對象。對象引用計數加1。如下是ARC和MRC等價的代碼
// ARC id obj = [[NSObject alloc] init]; void *p = (__bridge_retained void *)obj; // 等效 MRC 代碼 id obj = [NSObject alloc] init]; void *p = (void *)obj; [(id)p retain];
__bridge_transfer轉換:被轉換的變量所持有的對象在賦值給目標變量後隨之釋放。
// ARC ① (void)(__bridge_transfer id)p; //指定返回結果爲void ,不利用返回值 對象引用計數減1 ② id obj = (__bridge_transfer id)p; // 若是返回值賦值給目標變量,轉換後對象引用計數不變 // 等效 MRC 代碼 ① [p release]; ② id obj = (id)p; [obj retain];[(id)p release];
__strong修飾符:對對象實例的強引用,是id類型或對象類型的默認全部權修飾符。持有強引用的變量在超出其做用域時被廢棄,隨着強引用的失效,引用的對象
會隨之釋放。
// 如下兩行代碼是等效的 id obj = [[NSObject alloc] init]; id __strong obj = [[NSObject alloc] init];
__autoreleasing修飾符:對象變量要爲自動變量(包含局部變量、函數以及方法參數)。利用「__objc_autoreleasePoolPrint()」調試autoreleasepool上的對象
等價於MRC時調用對象的autorelease方法。如下兩段代碼等效:
// MRC NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; id obj = [[NSObject alloc] init]; [obj autorelease]; [pool drain]; // ARC @autoreleasepool { id __autoreleasing obj = [[NSObject alloc] init]; }
顯式的附加__autoreleasing修飾符和顯式度附加__strong修飾符同樣「罕見」,有些狀況下非
顯式的聲明__autoreleasing修飾符也是能夠的,這些狀況會自動將對象註冊到自動釋放池。
1.非本身生成並持有的對象
@autoreleasepool { id obj = [NSMutableArray array]; // 變量obj爲對象的強引用(默認__strong),編譯器判斷方法名後(非alloc/new等)自動註冊大autoreleasepool }
2.id的指針和對象的指針(二級指針)在沒有顯示的指定時會被附加上__autoreleasing修飾符。
NSError *error = nil; //全部權爲__strong類型 NSError **pError = &error; //編譯出錯, NSError * (對象)默認爲_autoreleasing類型,賦值先後全部權不匹配。 NSError * __strong *pError = &error; //編譯正確
3.本身生成並持有的對象做爲返回值
+ (id)array { id obj = [[NSMutableArray alloc] init]; return obj; }
4.雖然__weak修飾符是爲了不循環引用而使用的,但訪問附有__weak修飾符的變量時實j 際上一定要訪問註冊到autoreleasepool的對象爲了不該對象被廢棄,註冊到autoreleasepool以後能夠保證在autoreleasepool塊結束以前對象存在。
id __weak obj1 = obj0; NSLog(@"class = %@",[obj1 class]); // 如下代碼和以上代碼相同 id __weak obj1 = obj0; id __autoreleasing tmp = obj1; NSLog(@"class = %@",[tmp class]);
Apple 提供一些方法查看對象的引用計數,可是並不能徹底信任這些函數提供的引用計數值。對於已釋放的對象一級不正確的對象地址,有時 也返回」1「,在多線程中,由於存在競態條件的問題,因此取得的的數值不必定可信。
[object retainCount]; //獲得object的引用計數,此方法僅僅適用於MRC _objc_rootRetainCount(object); // ARC和MRC適用,頭文件聲明:OBJC_EXTERN int _objc_rootRetainCount(id);