在iOS開發中,幾乎天天都會遇到NSString
屬性的聲明,
在ARC內存管理機制下,
NSString屬性聲明有兩個關鍵字能夠選擇:strong
和copy
;
那麼問題來了,何時用strong
,何時用copy
?html
下面我寫一個小demo,但願你們能看懂,也還請路過的大神指教!git
我在.h文件中聲明瞭兩個NSString
屬性,以下:github
@property(nonatomic, strong) NSString *strongStr;
@property(nonatomic, copy) NSString *copyyStr;
// 注:不能以alloc,new,copy,mutableCopy 做爲開頭命名,好比:copyStr
複製代碼
// 第一種場景:用NSString直接賦值
NSString *originStr1 = [NSString stringWithFormat:@"hello,everyone"];
_strongStr = originStr1;
_copyyStr = originStr1;
NSLog(@"第一種場景:用NSString直接賦值");
NSLog(@" 對象地址 對象指針地址 對象的值 ");
NSLog(@"originStr: %p , %p , %@", originStr1, &originStr1, originStr1);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);
複製代碼
而後咱們運行一下,打印結果以下圖:ui
結論:這種狀況下,不論是用strong仍是copy修飾的對象,其指向的地址都是originStr的地址。atom
// 第二種場景:用NSMutableString直接賦值
NSMutableString *originStr2 = [NSMutableString stringWithFormat:@"hello,everyone"];
_strongStr = originStr2;
_copyyStr = originStr2;
[originStr2 setString:@"hello,QiShare"];
NSLog(@"第二種場景:用NSMutableString直接賦值");
NSLog(@" 對象地址 對象指針地址 對象的值 ");
NSLog(@"originStr: %p , %p , %@", originStr2, &originStr2, originStr2);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);
複製代碼
而後咱們運行一下,打印結果以下圖:spa
看到這裏,同窗們可能會有疑問,爲何不管是用
strong
仍是copy
修飾的對象,其指針指向的地址依然仍是originStr的地址
?爲何_copyyStr的值會變成「hello,QiShare」呢?不該該是「hello,everyone」嗎?
我們先不解釋,賣個關子,咱們接着往下看。3d
// 第三種場景:用NSMutableString點語法賦值
NSMutableString *originStr3 = [NSMutableString stringWithFormat:@"hello,everyone"];
self.strongStr = originStr3;
self.copyyStr = originStr3;
[originStr3 setString:@"hello,QiShare"];
NSLog(@"第三種場景:用NSMutableString點語法賦值");
NSLog(@" 對象地址 對象指針地址 對象的值 ");
NSLog(@"originStr: %p , %p , %@", originStr3, &originStr3, originStr3);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);
複製代碼
而後咱們運行一下,打印結果以下圖:指針
OK,這回咱們終於看到咱們但願看到的結果了,
_copyyStr依然是「hello,everyone」,沒有變成「hello,QiShare」,
_copyyStr指針指向的地址再也不是_originStr的地址。
細心的同窗會發現,第三種在賦值的時候用了點語法
,而不是直接賦值。
除了將 _strongStr = originStr2; 改成 self.strongStr = originStr3;
_copyyStr = originStr2;改成 self.copyyStr = originStr3;
其他徹底同樣。code
也就是說,咱們將_copyyStr = originStr2;改成 self.copyyStr = originStr3;才致使了_copyyStr的值在第三種狀況下依然沒有改變,這是爲何呢?orm
當咱們用@property來聲明屬性變量時,編譯器會自動爲咱們生成一個如下劃線加屬性名命名的實例變量(@synthesize copyyStr = _copyyStr),而且生成其對應的getter、setter方法。
當咱們用self.copyyStr = originStr賦值時,會調用coppyStr的setter方法,而_copyyStr = originStr 賦值時給_copyyStr實例變量直接賦值,並不會調用copyyStr的setter方法
,而在setter方法
中有一個很是關鍵
的語句:
_copyyStr = [copyyStr copy];
複製代碼
結論:第三種場景中用self.copyyStr = originStr 賦值時,調用copyyStr的setter方法,setter方法對傳入的copyyStr作了次
深拷貝
生成了一個新的對象賦值給_copyyStr,因此_copyyStr指向的地址和對象值都再也不和originStr相同。
// 第四種場景:用NSString點語法賦值
NSString *originStr4 = [NSString stringWithFormat:@"hello,everyone"];
self.strongStr = originStr4;
self.copyyStr = originStr4;
NSLog(@"第三種場景:用NSMutableString點語法賦值");
NSLog(@" 對象地址 對象指針地址 對象的值 ");
NSLog(@"originStr: %p , %p , %@", originStr4, &originStr4, originStr4);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);
複製代碼
這裏咱們將_copyyStr = originStr;改爲了self.copyyStr = originStr;
這時候打印結果會是什麼樣呢?
看了打印結果,可能有的同窗會產生疑問,爲何用了self.copyyStr = originStr進行賦值,調用了setter方法,調用了_copyyStr = [copyyStr copy]以後,_copyyName指向的地址和originStr指向的地址仍是相同的呢?
緣由:這裏的copy是淺拷貝,並無生成新的對象
由上面的例子能夠得出:
NSString
時,因爲是不可變字符串,因此,無論使用strong
仍是copy
修飾,都是指向原來的對象,copy
操做只是作了一次淺拷貝。NSMutableString
時,strong
只是將源字符串的引用計數加1
,而copy
則是對原字符串作了次深拷貝
,從而生成了一個新的對象,而且copy的對象指向這個新對象。另外須要注意的是,這個copy屬性對象的類型始終是NSString
,而不是NSMutableString,若是想讓拷貝過來的對象是可變的,就要使用mutableCopy
。NSMutableString
的時候,使用strong只會增長引用計數。內存浪費
。而若是原字符串是NSString時,strong和copy效果同樣,就不會有這個問題。可是,咱們通常聲明NSString時,也不但願它改變,因此通常狀況下,建議使用copy
,這樣能夠避免NSMutableString帶來的錯誤。
咱們都知道,assign
用來修飾基本數據類型,weak
用來修飾OC對象。
其實照理,assign
也能修飾OC對象
,可是assign
修飾的對象在該對象釋放後,其指針依然存在,不會被置爲nil——這就形成了一個很嚴重的問題:出現了野指針
。當訪問這個野指針時,指向了原地址,而原地址有兩種狀況:
可是用weak
來修飾的話,對象釋放的時候會把指針置爲nil
,從而避免了野指針
的出現。
那又有個疑問出現了,憑什麼基本數據類型就可使用assign
。這就要扯到堆
和棧
的問題了,基本數據類型會被分配到棧空間,而棧空間是由系統自動管理分配和釋放
的,就不會形成野指針的問題。
ps:本文demo連接:github.com/QiShare/QiS…
轉載自:https://www.jianshu.com/p/62913d6cbc40