級別: ★☆☆☆☆
標籤:「iOS」「NSString」「strong和copy」
做者: MrLiuQphp
在iOS開發中,幾乎天天都會遇到NSString屬性的聲明, 在ARC內存管理機制下, NSString屬性聲明有兩個關鍵字能夠選擇:strong和copy; 那麼問題來了,何時用copy,何時用strong?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);
複製代碼
而後咱們運行一下,打印結果以下圖: 微信
結論:這種狀況下,不論是用strong仍是copy修飾的對象,其指向的地址都是originStr的地址。ide
// 第二種場景:用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);
複製代碼
而後咱們運行一下,打印結果以下圖: atom
看到這裏,同窗們可能會有疑問,爲何不管是用strong仍是copy修飾的對象,其指針指向的地址依然仍是originStr的地址?爲何_copyyStr的值會變成「hello,QiShare」呢?不該該是「hello,everyone」嗎? 我們先不解釋,賣個關子,咱們接着往下看。spa
// 第三種場景:用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);
複製代碼
而後咱們運行一下,打印結果以下圖: 3d
OK,這回咱們終於看到咱們但願看到的結果了, _copyyStr依然是「hello,everyone」,沒有變成「hello,QiShare」, _copyyStr指針指向的地址再也不是_originStr的地址。 細心的同窗會發現,第三種在賦值的時候用了點語法,而不是直接賦值。 除了將 _strongStr = originStr2; 改成 self.strongStr = originStr3; _copyyStr = originStr2;改成 self.copyyStr = originStr3; 其他徹底同樣。指針
也就是說,咱們將_copyyStr = originStr2;改成 self.copyyStr = originStr3;才致使了_copyyStr的值在第三種狀況下依然沒有改變,這是爲何呢?
當咱們用@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時,也不但願它改變,因此通常狀況下,建議使用copy,這樣能夠避免NSMutableString帶來的錯誤。
咱們都知道,assign用來修飾基本數據類型,weak用來修飾OC對象。
其實照理,assign也能修飾OC對象,可是assign修飾的對象在該對象釋放後,其指針依然存在,不會被置爲nil——這就形成了一個很嚴重的問題:出現了野指針。當訪問這個野指針時,指向了原地址,而原地址是nil,因此會形成程序的crash。可是用weak來修飾的話,對象釋放的時候會把指針置爲nil,從而避免了野指針的出現。
那又有個疑問出現了,憑什麼基本數據類型就可使用assign。這就要扯到堆和棧的問題了,基本數據類型會被分配到棧空間,而棧空間是由系統自動管理分配和釋放的,就不會形成野指針的問題。
ps:本文demo連接
關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)
推薦文章:iOS UISlider數值與滑動聯動