ARC工做原理xcode
每次建立一個類的實例,ARC就會分配一個內存塊,用來存儲這個實例的相關信息。這個內存塊保存着實例的類型,以及這個實例相關的屬性的值。安全
當實例再也不被使用時,ARC釋放這個實例使用的內存,使這塊內存可做它用。這保證了類實例再也不被使用時,它們不會佔用內存空間。app
可是,若是ARC釋放了仍在使用的實例,那麼你就不能再訪問這個實例的屬性或者調用它的方法。若是你仍然試圖訪問這個實例,應用極有可能會崩潰。this
ARC的一個基本規則:只要某個對象被任一strong指針指向,那麼它將不會被銷燬。若是對象沒有被任何strong指針指向,那麼就將被銷燬。atom
一、strong關鍵字:與retain相似,引用計數自動+1。強引用,只要強持有某個實例,而且這個實例的強引用存在,就不能銷燬這個實例。默認下是強引用spa
下面的例子對 strong的使用,有很詳細的說明:指針
id __strong obj0 = [[NSObject alloc] init]; /* object A */ /** obj0 has a strong reference to object A */ id __strong obj1 = [[NSObject alloc] init]; /* object B */ /** obj1 has a strong reference to object B */ id __strong obj2 = nil; /** obj2 has no reference */ obj0 = obj1; /**Obj0 has a strong reference to object B, which has been assigned from obj1. * So, obj0 does not have a strong reference to object A anymore. * Object A is disposed of because no one has ownership of it.** At this moment, both obj0 and obj1 have strong references to object B. */ obj2 = obj0; /** Through obj0, obj2 has a strong reference to object B.** At this moment, obj0, obj1 and obj2 have strong references to object B. */ obj1 = nil; /** Because nil is assigned to obj1, strong references to object B disappear. ** At this moment, obj0 and obj2 have strong references to object B.*/ obj0 = nil; /** Because nil is assigned to obj0, a strong reference to object B disappears. ** At this moment, obj2 has a strong reference to object B.*/ obj2 = nil; /** Because nil is assigned to obj2, a strong reference to object B disappears. * Object B is disposed of because no one has ownership of it*/
id obj = [array objectAtIndex:0]; [array removeObjectAtIndex:0]; NSLog(@"%@",obj);
在MRC時代這幾行代碼應該就掛掉了,由於array中0號對象被remove之後就被當即銷燬了,所以obj指向了一個dealloced的對象,所以在NSLog的時候將出現EXC_BAD_ACCESS。而在ARC中因爲obj是strong的,所以它持有了array中的首個對象,array再也不是該對象的惟一持有者。即便咱們從array中將obj移除了,它也依然被別的指針持有,所以不會被銷燬。調試
二、weak關鍵字:與assign相似,但更聰明。弱引用類型的指針也能夠指向對象,可是並不會持有該對象。對象一旦被釋放,這些指向這個對象的weak指針都將被賦值爲nil。這樣的好處能有效的防止野指針。例如:code
__weak NSString *str = [[NSString alloc] initWithFormat:…]; NSLog(@"%@",str); //輸出是"(null)"
這兩句代碼沒有任何意義。因爲str是弱引用,不會持有alloc出來的對象。根據ARC的規則,一個對象沒有強引用,這個對象就會被釋放。orm
三、unsafe_unretained:顧名思義,不安全;與weak有點相似。指針也是能夠指向對象,可是並不會持有該對象。對象一旦被釋放,這些指向這個對象的unsafe_unretained指針並不會知道對象已被釋放,形成了野指針的出現,程序會crash。儘可能少用unsafe_unretained關鍵字,使用了必須手動去管理實例。例如:下面的代碼會崩潰
@property (nonatomic, strong) NSString *string1; @property (nonatomic, unsafe_unretained) NSString *string2; self.string1 = [[NSString alloc] initWithUTF8String:"string 1"]; self.string2 = self.string1; self.string1 = nil; NSLog(@"String 2 = %@", self.string2);
四、__autoreleasing:可使對像延遲釋放。好比你想傳一個未初始化地對像引用到一個方法當中,在此方法中實始化此對像,那麼這種狀況將是__autoreleasing表演的時候
注:弱引用只能聲明爲變量類型,由於運行時它的值可能改變。弱引用絕對不能聲明爲常量
五、循環引用:一個類的實例一直都有強引用以至實例沒法釋放;(1)在兩個類實例彼此保持對方的強引用,使得每一個實例都使對方保持有效時會發生這種狀況。咱們稱之爲循環引用。
id test0 = [[Test alloc] init]; id test1 = [[Test alloc] init]; [test0 setObject:test1]; [test1 setObject:test0];
(2)類的實例本身引用本身的實例。
id test = [[Test alloc] init]; [test setObject:test];
附錄:
EXC_BAD_ACCESS:這種狀況是對指針對象的過分釋放,致使次指針爲野指針報錯。這類的報錯有時候是很難找出報錯的位置的。從網上資料找出2種調試方法:
<1>經過NSZombieEnabled (這方法常用,但少數的仍是沒法查找出來)
在xcode中Run,Stop 右邊也就是選擇設備的地方左邊找到
Scheme >Edit Scheme>Arguments>Environment Variables下添加
NSZombieEnabled YES
MallocStackLoggingNoCompact YES
MallocStackLogging YES
<2>經過重寫object的respondsToSelector方法,顯示出EXC_BAD_ACCESS前訪問的最後一個對象
你須要在每一個object的.m或者.mm文件中加入上面代碼,而且在 other c flags中加入-D _FOR_DEBUG_(記住請只在Debug Configuration下加入此標記)。這樣當你程序崩潰時,Xcode的console上就會準確地記錄了最後運行的object的方法。
#ifdef _FOR_DEBUG_ -(BOOL) respondsToSelector:(SEL)aSelector { printf("SELECTOR: %s\n", [NSStringFromSelector(aSelector) UTF8String]); return [super respondsToSelector:aSelector]; } #endif