iOS strong和copy的區(qū)別

級別: ★☆☆☆☆
標簽:「iOS」「NSString」「strong和copy」
作者: MrLiuQ
審校: QiShare團隊

在iOS開發(fā)中摸吠,幾乎每天都會遇到NSString屬性的聲明,
ARC內存管理機制下,
NSString屬性聲明有兩個關鍵字可以選擇:strongcopy饵沧;
那么問題來了,什么時候用strong责球,什么時候用copy滤馍?

下面我寫一個小demo畏陕,希望大家能看懂配乓,也還請路過的大神指教!

我在.h文件中聲明了兩個NSString屬性惠毁,如下:

@property(nonatomic, strong) NSString *strongStr;

@property(nonatomic, copy) NSString *copyyStr;
// 注:不能以alloc犹芹,new,copy鞠绰,mutableCopy 作為開頭命名腰埂,比如:copyStr

第一種場景:用NSString直接賦值(錯誤案例)

// 第一種場景:用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的地址。


第二種場景:用NSMutableString直接賦值(錯誤案例)

// 第二種場景:用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);

然后我們運行一下翁巍,打印結果如下圖:


看到這里驴一,同學們可能會有疑問,為什么不論是用strong還是copy修飾的對象灶壶,其指針指向的地址依然還是originStr的地址肝断?為什么_copyyStr的值會變成“hello,QiShare”呢?不應該是“hello胸懈,everyone”嗎鱼蝉?
咱們先不解釋,賣個關子箫荡,我們接著往下看。


第三種場景:用NSMutableString點語法賦值

// 第三種場景:用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的地址低矮。
細心的同學會發(fā)現(xiàn),第三種在賦值的時候用了點語法被冒,而不是直接賦值军掂。
除了將 _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點語法賦值
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只是將源字符串的引用計數(shù)加1,而copy則是對原字符串做了次深拷貝谜诫,從而生成了一個新的對象漾峡,并且copy的對象指向這個新對象。另外需要注意的是喻旷,這個copy屬性對象的類型始終是NSString生逸,而不是NSMutableString,如果想讓拷貝過來的對象是可變的且预,就要使用mutableCopy槽袄。
    所以,如果源字符串是NSMutableString的時候辣之,使用strong只會增加引用計數(shù)掰伸。
    但是copy會執(zhí)行一次深拷貝,會造成不必要的內存浪費怀估。而如果原字符串是NSString時狮鸭,strong和copy效果一樣,就不會有這個問題多搀。

但是歧蕉,我們一般聲明NSString時,也不希望它改變康铭,所以一般情況下惯退,建議使用copy,這樣可以避免NSMutableString帶來的錯誤从藤。

順便路過提一下assign與weak

我們都知道催跪,assign用來修飾基本數(shù)據類型,weak用來修飾OC對象夷野。

其實照理懊蒸,assign也能修飾OC對象,但是assign修飾的對象在該對象釋放后悯搔,其指針依然存在骑丸,不會被置為nil——這就造成了一個很嚴重的問題:出現(xiàn)了野指針。當訪問這個野指針時,指向了原地址通危,而原地址有兩種情況:

  • 第一種情況:原地址沒有改變铸豁,代碼運行通過,但很有可能有邏輯bug菊碟。
  • 第二種情況:原地址已經改變节芥,結果不可預測,多數(shù)崩潰逆害,也有可能出現(xiàn)其他莫名錯誤藏古。

但是用weak來修飾的話,對象釋放的時候會把指針置為nil忍燥,從而避免了野指針的出現(xiàn)。

那又有個疑問出現(xiàn)了隙姿,憑什么基本數(shù)據類型就可以使用assign梅垄。這就要扯到的問題了,基本數(shù)據類型會被分配到検溏瑁空間队丝,而棧空間是由系統(tǒng)自動管理分配和釋放的欲鹏,就不會造成野指針的問題机久。

ps:本文demo鏈接:https://github.com/QiShare/QiStrongVsCopy


關注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)

推薦文章:iOS UISlider數(shù)值與滑動聯(lián)動

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市赔嚎,隨后出現(xiàn)的幾起案子膘盖,更是在濱河造成了極大的恐慌,老刑警劉巖尤误,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侠畔,死亡現(xiàn)場離奇詭異,居然都是意外死亡损晤,警方通過查閱死者的電腦和手機软棺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尤勋,“玉大人喘落,你說我怎么就攤上這事∽畋” “怎么了瘦棋?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長锌奴。 經常有香客問我兽狭,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任箕慧,我火速辦了婚禮服球,結果婚禮上,老公的妹妹穿的比我還像新娘颠焦。我一直安慰自己斩熊,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布伐庭。 她就那樣靜靜地躺著粉渠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪圾另。 梳的紋絲不亂的頭發(fā)上霸株,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音集乔,去河邊找鬼去件。 笑死,一個胖子當著我的面吹牛扰路,可吹牛的內容都是我干的尤溜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼汗唱,長吁一口氣:“原來是場噩夢啊……” “哼宫莱!你這毒婦竟也來了?” 一聲冷哼從身側響起哩罪,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤授霸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后际插,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绝葡,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年腹鹉,在試婚紗的時候發(fā)現(xiàn)自己被綠了藏畅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡功咒,死狀恐怖愉阎,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情力奋,我是刑警寧澤榜旦,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站景殷,受9級特大地震影響溅呢,放射性物質發(fā)生泄漏澡屡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一咐旧、第九天 我趴在偏房一處隱蔽的房頂上張望驶鹉。 院中可真熱鬧,春花似錦铣墨、人聲如沸室埋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姚淆。三九已至,卻和暖如春屡律,著一層夾襖步出監(jiān)牢的瞬間腌逢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工超埋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留上忍,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓纳本,卻偏偏與公主長得像,于是被迫代替她去往敵國和親腋颠。 傳聞我的和親對象是個殘疾皇子繁成,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容

  • 在iOS開發(fā)中,幾乎每天都會遇到NSString屬性的聲明淑玫,在ARC內存管理機制下巾腕,NSString屬性聲明有兩個...
    齊舞647閱讀 496評論 0 10
  • 1.ios高性能編程 (1).內層 最小的內層平均值和峰值(2).耗電量 高效的算法和數(shù)據結構(3).初始化時...
    歐辰_OSR閱讀 29,321評論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,089評論 1 32
  • 每日一題:數(shù)據持久化 安卓提供了三種方式用于簡單的數(shù)據持久化功能:文件儲存,SharedPreference存儲絮蒿,...
    林銳波閱讀 1,381評論 0 0
  • 一笑 從腐爛的泥土中 拔出的影子 比活著的肉體 要深沉復雜得多 只有在脫離肉身時 才能厘清斑...
    看云閣閱讀 181評論 0 0