OC基礎(chǔ) - strong和copy的區(qū)別【轉(zhuǎn)載】

經(jīng)過(guò)幾次面試,發(fā)現(xiàn)自己的基礎(chǔ)能力還是很差,很多知識(shí)點(diǎn)都是半懂不懂,Strong、copy、weak、assign的差別就是經(jīng)常被面試官問(wèn)到的一個(gè)問(wèn)題,所以我覺(jué)得很有必要回來(lái)看看。
本文轉(zhuǎn)載自 QiShareiOS strong和copy的區(qū)別

在iOS開(kāi)發(fā)中,幾乎每天都會(huì)遇到NSString屬性的聲明,
ARC內(nèi)存管理機(jī)制下,
NSString屬性聲明有兩個(gè)關(guān)鍵字可以選擇:strongcopy
那么問(wèn)題來(lái)了,什么時(shí)候用strong,什么時(shí)候用copy?

下面我寫(xiě)一個(gè)小demo,希望大家能看懂,也還請(qǐng)路過(guò)的大神指教!

我在.h文件中聲明了兩個(gè)NSString屬性,如下:

@property(nonatomic, strong) NSString *strongStr;

@property(nonatomic, copy) NSString *copyyStr;
// 注:不能以alloc,new,copy,mutableCopy 作為開(kāi)頭命名,比如:copyStr
第一種場(chǎng)景:用NSString直接賦值(錯(cuò)誤案例)
// 第一種場(chǎng)景:用NSString直接賦值
NSString *originStr1 = [NSString stringWithFormat:@"hello,everyone"];

_strongStr = originStr1;
_copyyStr = originStr1;
    
NSLog(@"第一種場(chǎng)景:用NSString直接賦值");
NSLog(@"               對(duì)象地址         對(duì)象指針地址        對(duì)象的值   ");
NSLog(@"originStr: %p , %p , %@", originStr1, &originStr1, originStr1);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);

然后我們運(yùn)行一下,打印結(jié)果如下圖:

結(jié)論:這種情況下,不管是用strong還是copy修飾的對(duì)象,其指向的地址都是originStr的地址。


第二種場(chǎng)景:用NSMutableString直接賦值(錯(cuò)誤案例)
// 第二種場(chǎng)景:用NSMutableString直接賦值
NSMutableString *originStr2 = [NSMutableString stringWithFormat:@"hello,everyone"];

_strongStr = originStr2;
_copyyStr = originStr2;

[originStr2 setString:@"hello,QiShare"];
    
NSLog(@"第二種場(chǎng)景:用NSMutableString直接賦值");
NSLog(@"               對(duì)象地址         對(duì)象指針地址        對(duì)象的值   ");
NSLog(@"originStr: %p , %p , %@", originStr2, &originStr2, originStr2);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);

然后我們運(yùn)行一下,打印結(jié)果如下圖:


看到這里,同學(xué)們可能會(huì)有疑問(wèn),為什么不論是用strong還是copy修飾的對(duì)象,其指針指向的地址依然還是originStr的地址?為什么_copyyStr的值會(huì)變成“hello,QiShare”呢?不應(yīng)該是“hello,everyone”嗎?

咱們先不解釋?zhuān)u(mài)個(gè)關(guān)子,我們接著往下看。

第三種場(chǎng)景:用NSMutableString點(diǎn)語(yǔ)法賦值
// 第三種場(chǎng)景:用NSMutableString點(diǎn)語(yǔ)法賦值
NSMutableString *originStr3 = [NSMutableString stringWithFormat:@"hello,everyone"];
    
self.strongStr = originStr3;
self.copyyStr = originStr3;
    
[originStr3 setString:@"hello,QiShare"];
    
NSLog(@"第三種場(chǎng)景:用NSMutableString點(diǎn)語(yǔ)法賦值");
NSLog(@"               對(duì)象地址         對(duì)象指針地址        對(duì)象的值   ");
NSLog(@"originStr: %p , %p , %@", originStr3, &originStr3, originStr3);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);

然后我們運(yùn)行一下,打印結(jié)果如下圖:

OK,這回我們終于看到我們希望看到的結(jié)果了,
_copyyStr依然是“hello,everyone”,沒(méi)有變成“hello,QiShare”,
_copyyStr指針指向的地址不再是_originStr的地址。
細(xì)心的同學(xué)會(huì)發(fā)現(xiàn),第三種在賦值的時(shí)候用了點(diǎn)語(yǔ)法,而不是直接賦值。
除了將 _strongStr = originStr2; 改為 self.strongStr = originStr3;
_copyyStr = originStr2;改為 self.copyyStr = originStr3;
其余完全一樣。
也就是說(shuō),我們將_copyyStr = originStr2;改為 self.copyyStr = originStr3;才導(dǎo)致了_copyyStr的值在第三種情況下依然沒(méi)有改變,這是為什么呢?
當(dāng)我們用@property來(lái)聲明屬性變量時(shí),編譯器會(huì)自動(dòng)為我們生成一個(gè)以下劃線加屬性名命名的實(shí)例變量(@synthesize copyyStr = _copyyStr),并且生成其對(duì)應(yīng)的getter、setter方法。
當(dāng)我們用self.copyyStr = originStr賦值時(shí),會(huì)調(diào)用coppyStr的setter方法,而_copyyStr = originStr 賦值時(shí)給_copyyStr實(shí)例變量直接賦值,并不會(huì)調(diào)用copyyStr的setter方法,而在setter方法中有一個(gè)非常關(guān)鍵的語(yǔ)句:

_copyyStr = [copyyStr copy];

結(jié)論:第三種場(chǎng)景中用self.copyyStr = originStr 賦值時(shí),調(diào)用copyyStr的setter方法,setter方法對(duì)傳入的copyyStr做了次深拷貝生成了一個(gè)新的對(duì)象賦值給_copyyStr,所以_copyyStr指向的地址和對(duì)象值都不再和originStr相同。

第四種場(chǎng)景:用NSString點(diǎn)語(yǔ)法賦值
// 第四種場(chǎng)景:用NSString點(diǎn)語(yǔ)法賦值
NSString *originStr4 = [NSString stringWithFormat:@"hello,everyone"];

self.strongStr = originStr4;
self.copyyStr = originStr4;
    
NSLog(@"第三種場(chǎng)景:用NSMutableString點(diǎn)語(yǔ)法賦值");
NSLog(@"               對(duì)象地址         對(duì)象指針地址        對(duì)象的值   ");
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;

這時(shí)候打印結(jié)果會(huì)是什么樣呢?

看了打印結(jié)果,可能有的同學(xué)會(huì)產(chǎn)生疑問(wèn),為什么用了self.copyyStr = originStr進(jìn)行賦值,調(diào)用了setter方法,調(diào)用了_copyyStr = [copyyStr copy]之后,_copyyName指向的地址和originStr指向的地址還是相同的呢?

原因:這里的copy是淺拷貝,并沒(méi)有生成新的對(duì)象

總結(jié):

由上面的例子可以得出:

  • 當(dāng)原字符串是NSString時(shí),由于是不可變字符串,所以,不管使用strong還是copy修飾,都是指向原來(lái)的對(duì)象,copy操作只是做了一次淺拷貝。
  • 而當(dāng)源字符串是NSMutableString時(shí),strong只是將源字符串的引用計(jì)數(shù)加1 而copy則是對(duì)原字符串做了次深拷貝,從而生成了一個(gè)新的對(duì)象,并且copy的對(duì)象指向這個(gè)新對(duì)象。另外需要注意的是,這個(gè)copy屬性對(duì)象的類(lèi)型始終是NSString,而不是NSMutableString,如果想讓拷貝過(guò)來(lái)的對(duì)象是可變的,就要使用mutableCopy
    所以,如果源字符串是NSMutableString的時(shí)候,使用strong只會(huì)增加引用計(jì)數(shù)。
    但是copy會(huì)執(zhí)行一次深拷貝,會(huì)造成不必要的內(nèi)存浪費(fèi)。而如果原字符串是NSString時(shí),strongcopy效果一樣,就不會(huì)有這個(gè)問(wèn)題。

但是,我們一般聲明NSString時(shí),也不希望它改變,所以一般情況下,建議使用copy,這樣可以避免NSMutableString帶來(lái)的錯(cuò)誤。

順便路過(guò)提一下assign與weak

我們都知道,assign用來(lái)修飾基本數(shù)據(jù)類(lèi)型,weak用來(lái)修飾OC對(duì)象。
其實(shí)照理,assign也能修飾OC對(duì)象,但是assign修飾的對(duì)象在該對(duì)象釋放后,其指針依然存在,不會(huì)被置為nil——這就造成了一個(gè)很?chē)?yán)重的問(wèn)題:出現(xiàn)了野指針。當(dāng)訪問(wèn)這個(gè)野指針時(shí),指向了原地址,而原地址有兩種情況:

  • 第一種情況:原地址沒(méi)有改變,代碼運(yùn)行通過(guò),但很有可能有邏輯bug。
  • 第二種情況:原地址已經(jīng)改變,結(jié)果不可預(yù)測(cè),多數(shù)崩潰,也有可能出現(xiàn)其他莫名錯(cuò)誤。

但是用weak來(lái)修飾的話,對(duì)象釋放的時(shí)候會(huì)把指針置為nil,從而避免了野指針的出現(xiàn)。
那又有個(gè)疑問(wèn)出現(xiàn)了,憑什么基本數(shù)據(jù)類(lèi)型就可以使用assign。這就要扯到堆和棧的問(wèn)題了,基本數(shù)據(jù)類(lèi)型會(huì)被分配到??臻g,而??臻g是由系統(tǒng)自動(dòng)管理分配和釋放的,就不會(huì)造成野指針的問(wèn)題。

求職廣告:本人實(shí)習(xí)生,現(xiàn)在急需一份工作,杭州南京合肥的都可以。對(duì)我感興趣的可以私聊我 0.0。謝謝~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容