ARC規(guī)則
?
所有權(quán)修飾符
? ? ARC有效時(shí),id 類(lèi)型和對(duì)象類(lèi)型同 C 語(yǔ)言其他類(lèi)型不同,類(lèi)型上必須附加所有權(quán)修飾符:
- __strong
- __weak
- __unsafe_unretained
- __autoreleasing
- __strong 是 id 和對(duì)象類(lèi)型默認(rèn)的所有權(quán)修飾符,沒(méi)明確規(guī)定時(shí)默認(rèn)為此
{
id __strong obj = [[NSObject alloc] init];
}
//ARC無(wú)效
{
id obj = [[NSObject alloc] init];
[obj release];
}
//以上兩個(gè)代碼一樣
//持有強(qiáng)引用的對(duì)象在超出其作用域時(shí)被廢棄,引用的對(duì)象隨之釋放
? ?取得非自己生成并持有的對(duì)象
id __strong obj = [NSMutableArray array];
//強(qiáng)引用所以持有
? ? __strong 修飾的變量,不僅在變量作用域,在賦值上也能夠正確管理對(duì)象所有者
? ? 在類(lèi)成員變量中,也可以在方法參數(shù)上使用附有 __strong 修飾符的變量
? ? __ strong,__ weak, __autoreleasing 可以保證將附有這些修飾符的自動(dòng)變量初始化為 nil
- __weak 是為了解決循環(huán)引用引起的內(nèi)存泄露,內(nèi)存泄漏是應(yīng)當(dāng)廢棄的對(duì)象在超出生存周期后繼續(xù)存在(對(duì)自身的強(qiáng)引用也會(huì)發(fā)生循環(huán)引用
id __weak obj = [[NSObject alloc] init];
//警告,生產(chǎn)對(duì)象會(huì)被立即釋放
id __strong obj0 = [[NSObject alloc] init];
id __weak obj1 = obj0
//可以
? ? 在持有某對(duì)象弱引用時(shí),若該對(duì)象被廢棄,則弱引用自動(dòng)失效并處于 nil
- __ unsafe_unretained 是不安全的所有權(quán)修飾符,修飾的變量不屬于編譯器的內(nèi)存管理對(duì)象
? ? 同 __weak 一樣,無(wú)法持有生成對(duì)象,但持有對(duì)象廢棄時(shí)不會(huì)歸為 nil,再訪問(wèn)很可能崩潰,在 iOS4 及 OS X Snow Leopardde 應(yīng)用程序中,必須用 unsafe 代替 weak,在通過(guò) unsafe 修飾的變量使用其對(duì)象時(shí),必須確保其確實(shí)存在
- __autoreleasing 當(dāng) ARC 有效時(shí)不能使用 autorelease 方法,也不能使用NSAutoreleasePool 類(lèi)
@autoreleasepool{
id __autoreleasing obj = [[NSObject alloc] init];
}
? ? 用 @autoreleasepool 塊來(lái)替代 NSAutoreleasePool 類(lèi)對(duì)象生產(chǎn)持有及廢棄
? 非顯示使用 __autoreleasing 的情況:
取得非自己生成并持有的對(duì)象時(shí),被注冊(cè)到 autoreleasepool ,編譯器會(huì)檢查方法名是否以 alloc/new/copy/mutableCopy 開(kāi)始,如果不是,對(duì)象作為函數(shù)的返回值將自動(dòng)注冊(cè)到 autoreleasepool
-
訪問(wèn) __weak 變量時(shí),必定要訪問(wèn)注冊(cè)到 autoreleasepool 的對(duì)象
id __weak obj1 = obj0; NSLog(@"class=%@", [obj1 class]); //等同于 id __weak obj1 = obj0; id __autoreleasing tmp = obj1; NSLog(@"class=%@", [tmp class]);
? ? 在訪問(wèn) __weak 變量引用的對(duì)象時(shí),對(duì)象可能被廢棄,把訪問(wèn)對(duì)象注冊(cè)到autoreleasepool,那么在 @autoreleasepool 塊結(jié)束前都能確保存在
-
id 指針 id *obj 和對(duì)象指針 NSObject **obj 在沒(méi)有顯示指定時(shí)會(huì)附加 __autoreleasing
等同于 id _autoreleasing *obj 和 NSObject * _autoreleasing *obj
NSError *error = nil; NSError **pError = &error; //編譯錯(cuò)誤 //賦值給對(duì)象指針時(shí),所有權(quán)修飾符必須一致 NSError *error = nil; NSError *__strong *pError = &error; //編譯正常
?
規(guī)則
-
不能使用 retain/release/retainCount/autorelease
內(nèi)存管理是編譯器的工作
不能使用 NSAllocateObject/NSDeallocateObject
需遵循內(nèi)存管理的方法命名規(guī)則
? ? alloc/new/copy/mutableCopy 開(kāi)始命名的方法返回對(duì)象時(shí),必須返回給調(diào)用方所應(yīng)當(dāng)持有的對(duì)象
? ? init 開(kāi)始的方法必須是實(shí)例方法,必須返回對(duì)象,應(yīng)為 id 類(lèi)型或方法聲明類(lèi)的對(duì)象類(lèi)型,或該類(lèi)的超類(lèi)或子類(lèi),不注冊(cè)到 autoreleasepool 上,只是對(duì) alloc 方法返回值的對(duì)象進(jìn)行初始化處理并返回。
-(void) initialize 不包含在上述規(guī)則里
- 不要顯式調(diào)用 delloc
? ? 不管 ARC 是否有效,對(duì)象廢棄時(shí)都會(huì)調(diào)用 delloc,有效時(shí)不必寫(xiě) [super delloc]
使用 @autoreleasepool 代替
不能使用 NSZone
? ? 不管 ARC 是否有效,區(qū)域在現(xiàn)在的運(yùn)行時(shí)系統(tǒng)都已被忽略
- 對(duì)象型變量不能作為 C 語(yǔ)言結(jié)構(gòu)體的成員
? ? 因?yàn)?ARC 把內(nèi)存管理的工作分配給編譯器,所以編譯器必須能知道并管理對(duì)象的生存周期,但 C 語(yǔ)言無(wú)法管理結(jié)構(gòu)體成員的生存周期。要把對(duì)象加入結(jié)構(gòu)體成員中,可強(qiáng)制轉(zhuǎn)換為 void* 或附加 __unsafe_unretained (注意避免內(nèi)存泄露)
- 顯示轉(zhuǎn)換 id 和 void*
/* ARC 無(wú)效 */
id obj = [[NSObject alloc] init];
void *p = obj;
id o = p;
[o release];
//id 和 void* 相互轉(zhuǎn)換沒(méi)問(wèn)題,但 ARC 有效時(shí)編譯錯(cuò)誤
/* ARC 有效 */
id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;
//id 或?qū)ο笮唾x給 void* 或者逆向賦值,都需要進(jìn)行特定轉(zhuǎn)換,如果只想單純賦值可以用"__bridge 轉(zhuǎn)換"
? ? 但轉(zhuǎn)換為 void* 的 __ bridge 轉(zhuǎn)換,安全性和 ___unsafe_unretained 相近,甚至更低
? ? 另兩種 __ bridge 轉(zhuǎn)換," __ bridge_retained 轉(zhuǎn)換"和" __ bridge_transfer 轉(zhuǎn)換"
//__bridge_retained 轉(zhuǎn)換
id obj = [[NSObject alloc] init];
void *p = (__bridge_retained void *)obj;
//使轉(zhuǎn)換賦值的變量也持有所賦值的對(duì)象\
/* ARC 無(wú)效 */
id obj = [[NSObject alloc] init];
void *p = obj;
[(id)p retain];
//__ bridge_transfer轉(zhuǎn)換
id obj = (__bridge_transfer id)p;
//與 retained 相反,被轉(zhuǎn)換的變量在持有對(duì)象被賦值給轉(zhuǎn)換變量后隨之釋放
/* ARC 無(wú)效 */
id obj = (id)p;
[obj retain];
[(id)p release];
? ? 如果使用以上兩種轉(zhuǎn)換,那么不使用 id 型或?qū)ο笮妥兞恳部梢陨沙钟屑搬尫艑?duì)象,雖然可以,但不推薦
? ? 這些轉(zhuǎn)換多用在 Object-C 對(duì)象和 Core Foundation 對(duì)象之間
Core Foundation 對(duì)象和 Object-C 對(duì)象區(qū)別很小,只是由哪個(gè)框架(Core Foundation 框架還是 Foundation 框架)生成。無(wú)論是哪種框架生成的對(duì)象,一旦生成,就能在不同框架中使用。兩者的轉(zhuǎn)換不需要額外的 CPU 資源,因此稱(chēng)為“免費(fèi)橋”(Toll-Free Bridge)
? ? 可以使用以下函數(shù)轉(zhuǎn)換 Object-C 對(duì)象和 Core Foundation 對(duì)象
CFTypeRef CFBridgingRetain(id X){
return (__bridge_retained CFTypeRef)X;
}
id CFBridgingRelease(CFTypeRef X){
return (__bridge_transfer id)X;
}
/* Object-C 對(duì)象轉(zhuǎn)為 Core Foundation 對(duì)象 */
CFMutableArrayRef cfObject = NULL;
{
id obj = [[NSMutableArray alloc] init];
cfObject = CFBridgingRetain(obj);
CFShow(cfObject);
printf("retain count = %d\n",CFGetRetainCount(cfObject));
//=2
}
printf("retain count after the scope = %d\n", CFGetRetainCount(cfObject)); //=1
CFRelease(cfObject);
//也可以用 __ bridge_retained 轉(zhuǎn)換替代 CFBridgingRetain
//用 __bridge 轉(zhuǎn)換替代會(huì)造成懸垂指針
/*Core Foundation 對(duì)象轉(zhuǎn)為 Object-C 對(duì)象 */
{
CFMutableArrayRef cfObject = CFArrayCreateMutable(kCFAlloctorDefault, 0, NULL);
prinft("retain count = %d\n",CFGetRetainCount(cfObject));
//=1
id obj = CFbridgingRelease(cfObject);
printf("retain count after the cast = %d\n", CFGetRetainCount(cfObject)); //=1
NSLog(@"class=%@",obj);
}
//可用 __bridge_transfer 轉(zhuǎn)換替代 CFbridgingRelease
//用 __bridge 轉(zhuǎn)換替代會(huì)造成內(nèi)存泄漏
?
屬性
? ? ARC 有效時(shí),Object-C 類(lèi)的屬性也會(huì)發(fā)生變化
@property (nonatomic, strong) NSString *name;
| 屬性聲明的屬性 | 所有權(quán)修飾符 |
|---|---|
| assign | __unsafe_unretained |
| copy | __strong(賦值的是被復(fù)制的對(duì)象) |
| retain | __strong |
| strong | __strong |
| unsafe_unretained | __unsafe_unretained |
| weak | __weak |
? ? 以上各屬性賦值給指定的屬性中就相當(dāng)于賦值給附加各屬性對(duì)應(yīng)的所有權(quán)修飾符的變量中。只有 copy 不是簡(jiǎn)單的復(fù)制,是通過(guò) NSCopying 接口的copyWithZone: 方法復(fù)制賦值源所生成的對(duì)象
? ? 在聲明類(lèi)成員變量時(shí),同屬性聲明中的屬性不一致會(huì)引起編譯錯(cuò)誤
?
數(shù)組
//附有 __strong 修飾符的變量作為靜態(tài)數(shù)組使用
id objs[10];
//__ weak, __ autoreleasing, __ unsafe_unretained 也相同
? ? 除 __ unsafe_unretained 以外的修飾符保證其指定的變量初始化為 nil
? ? 數(shù)組變量超出作用域時(shí),數(shù)組中各個(gè)賦有 __strong 修飾符的變量也隨之失效,對(duì)象也隨之釋放
? ? 動(dòng)態(tài)數(shù)組時(shí),會(huì)選擇使用 NSMutableArray,NSMutableDictionary,NSMutableSet 等 Foundation 框架的容器,這些容器會(huì)恰當(dāng)?shù)爻钟凶芳拥膶?duì)象并管理
? ? 但也可以使用賦有 __strong 修飾符的變量聲明動(dòng)態(tài)數(shù)組,需要遵守一些事項(xiàng)
? ? 聲明動(dòng)態(tài)數(shù)組用指針
id __strong *array = nil;
? ? 因?yàn)椤?id *類(lèi)型” 默認(rèn)為“ id __ autoreleasing *類(lèi)型”,所以必須顯式指定為strong,以及雖然保證了賦有 __ strong 的 id 型變量被初始化為 nil,但不保證賦有__strong 的 id 指針型變量初始化為 nil
? ? 使用類(lèi)名時(shí)如下記述
NSObject * __strong *array = nil;
? ? 使用 calloc 函數(shù)確保想要分配的附有 __strong 修飾符變量的容量占有的內(nèi)存塊
//初始化
array = (id __strong *)calloc(entries, sizeof(id));
? ? 該代碼分配了 entries 格所需內(nèi)存塊,由于使用賦有 __strong 的變量前必須初始化為 nil,所以這里使用使分配區(qū)域初始化為 0 的 calloc 函數(shù)來(lái)分配內(nèi)存,也可以用 malloc 后用 memset 等將其填充為 0
//這樣很危險(xiǎn)
array = (id __strong *)malloc(entries, sizeof(id));
for(NSUInteger i = 0; i < entries; i++)
array[i] = nil;
? ? 這樣內(nèi)存區(qū)域沒(méi)有初始化為 0,nil 會(huì)被賦值給 __strong 并被賦值了隨機(jī)地址的變量中,從而釋放一個(gè)不存在的變量,因此推薦使用 calloc
? ? 通過(guò) calloc 函數(shù)分配的動(dòng)態(tài)數(shù)組能完全像靜態(tài)數(shù)組一樣使用
array[0] = [[NSObject alloc] init];
? ? 但動(dòng)態(tài)數(shù)組中操作賦有 __strong 的變量,需要自己釋放所有元素
? ? 在只是簡(jiǎn)單地用 free 函數(shù)(即 free(array);)廢棄數(shù)組用內(nèi)存塊的情況下,數(shù)組各元素所賦值的對(duì)象不能再次釋放,從而引起內(nèi)存泄露
? ? 因?yàn)樵陟o態(tài)數(shù)組中,編譯器能夠根據(jù)變量作用域自動(dòng)插入釋放賦值對(duì)象的代碼,而在動(dòng)態(tài)數(shù)組中,編譯器不能確定數(shù)組的生存周期,無(wú)從處理 。一定要將 nil 賦值給所有元素,使得其所賦值對(duì)象的強(qiáng)引用失效,從而釋放對(duì)象,之后再 free
//釋放
for(NSUInteger i = 0; i < entries; i++)
array[i] = nil;
free(array);
? ? 同初始化的注意事項(xiàng)相反,即使用 memset 將內(nèi)存填充為 0 也不會(huì)釋放所賦值的對(duì)象,會(huì)引起內(nèi)存泄露
? ? 同樣禁止使用 memcpy 函數(shù)拷貝數(shù)組元素以及 realloc 函數(shù)重新分配內(nèi)存塊