iOS詳解assign、weak、retain、strong、copy、mutableCopy
先講講assign、retain、strong、weak
- assign
@property (nonatomic,assign)NSMutableArray *arr; self.arr = [NSMutableArray array]; [self.arr addObject:@"11"]; //執行結果: Thread 1: EXC_BAD_ACCESS
這裡會直接報記憶體錯誤,原因是因為,assign是用來修飾基本資料型別的,例如CGFloat、NSInteger、Int、float等等,因為在OC中assign修飾的物件引用計數不會加+1,這個物件會被立即釋放,而且在釋放之後不會置為nil,會保留物件的指標,會形成野指標,這個時候對其傳送訊息就會崩潰,就會報壞記憶體的錯誤。
- weak
@property (nonatomic,weak)NSMutableArray *weakArr; self.weakArr = [NSMutableArray array]; [self.weakArr addObject:@"1"]; NSLog(@"%@",self.weakArr); //執行結果: 2018-09-19 16:51:47.901018+0800 Demo[1649:254177] (null)
在OC中Weak用來修飾物件的,如果用來修飾基本資料型別會報紅,用weak修飾的物件在引用完畢後會被立即置為nil 而OC向nil傳送訊息是不會崩潰的。
這裡提一下assign和weak修飾的物件的引用計數都是不會加1的
- retain和strong
@property (nonatomic,strong)NSMutableArray *strongArr; self.strongArr = [NSMutableArray array]; [self.strongArr addObject:@"11"]; NSLog(@"%@",self.strongArr); //執行結果: 2018-09-19 16:56:57.389876+0800 Demo[1652:255035] ( 11 )
[NSMutableArray array]這句程式碼可以理解為創造了一個物件A,此時A的引用計數為1,self.strongArr做為物件B,把A賦值給B的時候,A的引用計數加1,此時A的引用計數為2,B指向了A,然後編譯器會自動對A進行釋放操作(因為是區域性變數),A的引用計數-1。在擁有B的物件不釋放的時候,A的引用計數永遠不可能為0,除非你手動釋放或者把B指向一個新的物件,這樣A永遠不會被釋放,這就是所謂的強引用。retain是在MRC中用到的修飾詞,在ARC中便用strong代替了。retain現在同strong,就是指標指向值地址,同時進行引用計數加1。
因為copy涉及的情況比較多,所以接下來單獨講講copy,主要分為以下幾種情況
在說copy與mutableCopy之前我們先看看官方文件對深拷貝與淺拷貝的闡釋,如下

深拷貝:
物件拷貝 - 重新申請一片記憶體保留這個物件,與原物件之間沒有半點關係。
淺拷貝:
指標拷貝 - 實際上相當於引用計數+1,被拷貝的和拷貝的引用同一個物件。
接下來我們分兩個方面做測試:
- 非集合不可變物件(copy、mutableCopy)
NSString *str = [NSString stringWithFormat:@"111"]; NSString *copyStr = [str copy]; NSString *mCopyStr = [str mutableCopy]; NSLog(@"%@ ---- %p",str,str); NSLog(@"%@ ---- %p",copyStr,copyStr); NSLog(@"%@ ---- %p",mCopyStr,mCopyStr); str = @"222"; NSLog(@"%@ ---- %p",str,str); NSLog(@"%@ ---- %p",copyStr,copyStr); NSLog(@"%@ ---- %p",mCopyStr,mCopyStr); //執行結果: 2018-09-19 17:25:12.102270+0800 Demo[1689:260186] 111 ---- 0xa000000003131313 2018-09-19 17:25:12.102387+0800 Demo[1689:260186] 111 ---- 0xa000000003131313 2018-09-19 17:25:12.102418+0800 Demo[1689:260186] 111 ---- 0x2827aa9d0 2018-09-19 17:25:12.102445+0800 Demo[1689:260186] 222 ---- 0x100776020 2018-09-19 17:25:12.102491+0800 Demo[1689:260186] 111 ---- 0xa000000003131313 2018-09-19 17:25:12.102518+0800 Demo[1689:260186] 111 ---- 0x2827aa9d0
這裡我們可以看到str和copyStr的地址是相同的,而mCopyStr是開闢了新的記憶體的。在 str = @"222"
這句程式碼後,因為str是不可變的、不可被修改,所以重新初始化。而這裡的copy為淺拷貝,mutableCopy為深拷貝。
- 非集合可變物件(copy、mutableCopy)
NSMutableString *mStr = [NSMutableString stringWithFormat:@"111"]; NSString *copyStr = [mStr copy]; NSString *mCopyStr = [mStr mutableCopy]; NSLog(@"%@ ---- %p",mStr,mStr); NSLog(@"%@ ---- %p",copyStr,copyStr); NSLog(@"%@ ---- %p",mCopyStr,mCopyStr); [mStr appendString:@",222"]; NSLog(@"%@ ---- %p",mStr,mStr); NSLog(@"%@ ---- %p",copyStr,copyStr); NSLog(@"%@ ---- %p",mCopyStr,mCopyStr); //執行結果: 2018-09-19 17:38:08.698067+0800 Demo[1696:261980] 111 ---- 0x2831745a0 2018-09-19 17:38:08.698234+0800 Demo[1696:261980] 111 ---- 0xa000000003131313 2018-09-19 17:38:08.698280+0800 Demo[1696:261980] 111 ---- 0x283174450 2018-09-19 17:38:08.698323+0800 Demo[1696:261980] 111,222 ---- 0x2831745a0 2018-09-19 17:38:08.698391+0800 Demo[1696:261980] 111 ---- 0xa000000003131313 2018-09-19 17:38:08.698432+0800 Demo[1696:261980] 111 ---- 0x283174450
對非集合可變物件進行copy、mutableCopy時,可以看到其記憶體地址都發生了變化,所以這裡的copy、mutableCopy都為深拷貝,對原有mStr操作,不會影響拷貝之後的值。
- 集合可變物件(copy、mutableCopy)
NSMutableArray *arrM = [NSMutableArray arrayWithObject:@"11"]; NSMutableArray *copyArrM = [arrM copy]; NSMutableArray *mCopyArrM = [arrM mutableCopy]; NSLog(@"%@ ---- \n%p",arrM,arrM); NSLog(@"%@ ---- \n%p",mCopyArrM,mCopyArrM); NSLog(@"%@ ---- \n%p",copyArrM,copyArrM); [arrM addObject:@"22"]; NSLog(@"%@ ---- \n%p",arrM,arrM); NSLog(@"%@ ---- \n%p",mCopyArrM,mCopyArrM); NSLog(@"%@ ---- \n%p",copyArrM,copyArrM); //執行結果: 2018-09-19 17:52:49.603180+0800 Demo[1708:264782] ( 11 ) ---- 0x282316d90 2018-09-19 17:52:49.603424+0800 Demo[1708:264782] ( 11 ) ---- 0x282317540 2018-09-19 17:52:49.603533+0800 Demo[1708:264782] ( 11 ) ---- 0x282f4c6a0 2018-09-19 17:52:49.603603+0800 Demo[1708:264782] ( 11, 22 ) ---- 0x282316d90 2018-09-19 17:52:49.603660+0800 Demo[1708:264782] ( 11 ) ---- 0x282317540 2018-09-19 17:52:49.603716+0800 Demo[1708:264782] ( 11 ) ---- 0x282f4c6a0
- 集合不可變物件(copy、mutableCopy)
NSArray *arr = [NSArray arrayWithObject:@"11"]; NSArray *copyArr = [arr copy]; NSMutableArray *mCopyArr = [arr mutableCopy]; NSLog(@"%@ ---- \n%p",arr,arr); NSLog(@"%@ ---- \n%p",copyArr,copyArr); NSLog(@"%@ ---- \n%p",mCopyArr,mCopyArr); //執行結果: 2018-09-19 17:57:24.646701+0800 Demo[1717:266299] ( 11 ) ---- 0x2808b81d0 2018-09-19 17:57:24.646878+0800 Demo[1717:266299] ( 11 ) ---- 0x2808b81d0 2018-09-19 17:57:24.647042+0800 Demo[1717:266299] ( 11 ) ---- 0x2804e1ef0
其實對集合不可變物件、集合可變物件進行copy和mutableCopy結果可以參考非集合不可變物件、非集合可變物件的結果
最後對delegate為什麼要用weak修飾簡單談下個人的看法
delegate在平常的使用中一般如下:

VC中擁有一個View,那麼View的引用計數+1,而View中有delegate,那麼delegate的引用計數+1。接著我們會在VC中有 view.delegate = self
,那麼如果view用strong修飾,此時引用計數+1,此時view的引用計數為2。那麼當VC銷燬時,view的引用計數-1,此時view的引用計數為1。那麼view是無法銷燬的,然後view又引用著delegate,所以view和delegate一直會在記憶體中的。那麼如果用weak修飾delegate的,view的引用計數就為1,等到VC銷燬的時候,view計數-1,也回跟著銷燬,那麼view引用的delegate也會銷燬。
以上是個人的一點愚見,如有錯誤,歡迎大家指正。