copy修飾NSArray strong修飾NSMutableArray

假裝很認(rèn)真的樣子

陰差陽錯伏伯,前兩天和一個小伙伴在一起聊天。聊到關(guān)于 copystrong 的問題。這個在ARC[Automatic Reference Counting)]下慢慢淡化的一個東東。交流之中讓我受益匪淺,原來 copystrong 還可以這么玩拗窃。


以下內(nèi)容在 demo 中均有體現(xiàn)

1.首先我們先看一下到底出現(xiàn)了什么問題

使用 copy 修飾這個可變數(shù)組
@property (copy, nonatomic) NSMutableArray *copAry;

// 直接崩潰測試
- (void)testCash {
    NSMutableArray *arr = [NSMutableArray arrayWithObjects:@1, @2, @3, nil];
    self.copAry = arr;
    [self.copAry removeObject:@1]; // 直接崩潰
    NSLog(@"self.copyAry = %@", self.copAry);
}
報錯reason: '-[__NSArrayI removeObject:]: unrecognized selector sent to instance 0x6000000487c0'

那么為什么使用 strong 來修飾就不會有這個問題呢。
我們來看一個例子:

  • 定義一個CJPerson類泌辫,.m 中先不實(shí)現(xiàn) setterr 方法

    CJPerson.h

  • 在控制器看一下打印結(jié)果

    NSMutableArray *names = [@[@"zhangsan"] mutableCopy];
    CJPerson *person = [[CJPerson alloc] init];
    person.sAry = names;// strong
    person.cAry = names;// copy
    
    [names addObject:@"lisi"];
    NSLog(@"sAry = %@, cAry = %@", person.sAry, person.cAry);
    /* 輸出結(jié)果:
     sAry = (
         zhangsan,
         lisi
     ), cAry = (
         zhangsan
     )
     */
  • 歸根結(jié)底之所以出現(xiàn)這樣問題随夸,那是因?yàn)?ARC 情況下 strongcopy 對屬性 setter 方法重寫的區(qū)別。
    strong: setter 時調(diào)用了 [sAry retain] 方法震放,實(shí)現(xiàn)了指針拷貝宾毒,也就是淺拷貝。
    copy: setter 時調(diào)用了 [cAry copy] 方法殿遂,實(shí)現(xiàn)了內(nèi)容拷貝诈铛,也就是深拷貝。
    也就是在 .m 文件中系統(tǒng)默認(rèn)幫我們實(shí)現(xiàn)的是:
    CJPerson.m
  • 具體可以看內(nèi)存地址圖詳解:(簡單粗糙墨礁,歡迎大神指正)


    內(nèi)存地址分析.png

2.那么這個與 copy 修飾 NSMutableArray 導(dǎo)致崩潰有什么關(guān)系呢幢竹?

原來不管是集合類對象(NSArray,NSDictionary,NSSet...),還是非集合類對象(NSString),接收到copy或者mutableCopy消息時恩静,都需遵循以下準(zhǔn)則:

  • copy 返回的都是不可變對象焕毫,所以如果對 copy 返回值去調(diào)用可變對象的接口就會 crash
  • mutableCopy 返回的都是可變對象
    所以在 - (void)testCash 方法中執(zhí)行到 self.copAry = arr; ARC 環(huán)境下setter 方法執(zhí)行了 copy 方法蹲坷,導(dǎo)致原本 NSMutableArray類型數(shù)組變成 NSArray 類型,在調(diào)用removeObject:方法時邑飒,自然會出現(xiàn)這個錯誤reason: '-[__NSArrayI removeObject:]: unrecognized selector sent to instance 0x6000000487c0'

3.那么 NSArray 類型為什么需要使用 copy 來修飾

  • 我們繼續(xù)使用 copystrong 來定義變量
@property (strong, nonatomic) NSArray *arr1;
@property (copy, nonatomic) NSArray *arr2;
// 為什么 NSArray 需要使用 copy
- (void)testUserCopyWithAry {
    NSMutableArray *arr = [NSMutableArray arrayWithObjects:@(1), @(2), @(3), nil];
    self.arr1 = arr;
    self.arr2 = arr;
    
    [arr addObject:@(4)];
    NSLog(@"arr1 = %@, arr2 = %@", self.arr1, self.arr2);
    
    /* 輸出結(jié)果:
     arr1 = (
         1,
         2,
         3,
         4
     ), arr2 = (
         1,
         2,
         3
     )
     */
}

簡直白的說就是:如果定義一個數(shù)組循签,使用 strong 來修飾的話,如果這個數(shù)組在外界被修改的話疙咸,那么這個用 strong 修改的數(shù)組變量的值也會跟著變化县匠。為什么?還是因?yàn)?strong 進(jìn)行了指針拷貝撒轮。在內(nèi)存中乞旦,兩個變量指向的是同一塊內(nèi)存地址。所以為了避免值在外接發(fā)生改變而影響自身值的變化腔召,我們通常選擇使用 copy 進(jìn)行修飾杆查。

4.接著再上兩個測試?yán)樱瑢Ρ瓤摧敵鼋Y(jié)果

- (void)test01 {
    NSArray *array = @[@1,@2,@3,@4];
    
    NSArray *copyArr = [array copy];
    NSArray *mCopyArr = [array mutableCopy];
    
    NSMutableArray *mcArr = [array copy];
    NSMutableArray *mmCopyArr = [array mutableCopy];
    
    NSLog(@"array:%p--copyArr:%p--mCopyArr:%p--mcArr:%p---mmCopyArr:%p",array,copyArr,mCopyArr,mcArr,mmCopyArr);
    
    /*  輸出結(jié)果
     array:     0x60000024ce10
     copyArr:   0x60000024ce10
     mCopyArr:  0x60000024cd80
     mcArr:     0x60000024ce10
     mmCopyArr: 0x60000024ce70
     */
    
}

- (void)test02 {
    NSArray *tarray = @[@1,@2,@3,@4];
    NSMutableArray *array = [[NSMutableArray alloc] init];
    [array addObjectsFromArray:tarray];
    
    NSArray *copyArr = [array copy];
    NSArray *mCopyArr = [array mutableCopy];
    
    NSMutableArray *mcArr = [array copy];
    NSMutableArray *mmCopyArr = [array mutableCopy];
    
    NSLog(@"array:%p--copyArr:%p--mCopyArr:%p--mcArr:%p---mmCopyArr:%p",array,copyArr,mCopyArr,mcArr,mmCopyArr);
    /* 輸出結(jié)果
     array:     0x60000024cd20
     copyArr:   0x60000024cc90
     mCopyArr:  0x60000024ce40
     mcArr:     0x60000024cde0
     mmCopyArr: 0x60000024d020
     */
}

小結(jié)一下:

  • NSArray的copy ---->指針拷貝---->淺拷貝
  • NSArray的mutableCopy臀蛛,NSMutableArray的copy, NSMutableArray的mutableCopy 均為深拷貝,即內(nèi)容拷貝崖蜜。
  • 關(guān)于NSString(非集合類對象),NSDictionary及其對應(yīng)的可變類型都可以此類推浊仆。

5.淺拷貝、深拷貝

全篇在講淺拷貝豫领、深拷貝抡柿,追究他們追究到底是什么。
段子手的理解:淺復(fù)制好比你和你的影子等恐,你完蛋洲劣,你的影子也完蛋。深復(fù)制好比你和你的克隆人课蔬,你完蛋囱稽,你的克隆人還活著。
淺拷貝

  • 淺拷貝就是對內(nèi)存地址的復(fù)制二跋,目標(biāo)對象和原對象指向同一片內(nèi)存地址战惊。
    注意
    多個對象共用一塊地址時,當(dāng)內(nèi)存銷毀的時候扎即,指向這片內(nèi)存地址的幾個指針需要重新定義才可以使用吞获,否則出現(xiàn)野指針現(xiàn)象。
    iOS的淺拷貝 中谚鄙,通常會使用 retain 關(guān)鍵字進(jìn)行引用計(jì)數(shù)各拷。因?yàn)樗瓤梢宰寧讉€指針共同指向同一內(nèi)存地址,也可以在 release 的時候 由于計(jì)數(shù)的存在闷营,不會讓內(nèi)存銷毀烤黍,從而出現(xiàn)野指針的現(xiàn)象。

深拷貝

  • 深拷貝也就是內(nèi)容拷貝。目標(biāo)對象雖然和原對象的值一樣蚊荣,但是所指向的內(nèi)存地址不一樣初狰。可以說深拷貝把原對象地址也拷貝了互例,而內(nèi)存地址是自主分配的奢入。因內(nèi)存地址不一樣,兩個對象也就互不影響媳叨、互不干涉了腥光。
    iOS的深拷貝 中,通常會使用 copymutableCopy 方法
// 深拷貝
- (void)testDeepCopy {
    NSString *str = @"abcdefg";
    NSString *cStr = [str copy];
    NSMutableString *mStr = [str mutableCopy];
    [mStr appendString:@"!!"];
    NSLog(@"\n str = %@ = %p,\n cStr = %@ = %p,\n mStr = %@ = %p", str, str, cStr, cStr, mStr, mStr);
}
/* 輸出結(jié)果:
 str = abcdefg = 0x109dd8090,
 cStr = abcdefg = 0x109dd8090,
 mStr = abcdefg!! = 0x604000057e50
*/

再次驗(yàn)證了第 4 個模塊


番外篇
淺拷貝的 retain 和 深拷貝中提到的 copy 有什么區(qū)別呢
可以觀看這篇文章:copy 和 retain 到底有啥區(qū)別

6.拷貝構(gòu)造

當(dāng)然在 iOS 中并不是所有的對象都支持copy糊秆,mutableCopy武福,遵守 NSCopying 協(xié)議的類可以發(fā)送 copy 消息,遵守NSMutableCopying 協(xié)議的類才可以發(fā)送 mutableCopy 消息痘番。
假如發(fā)送了一個沒有遵守上訴兩協(xié)議而發(fā)送 copy 或者 mutableCopy, 那么就會發(fā)生異常捉片。但是默認(rèn)的 iOS 類并沒有遵守這兩個協(xié)議。如果想自定義一下 copy 那么就必須遵守 NSCopying, 并且實(shí)現(xiàn) copyWithZone: 方法汞舱,如果想自定義一下mutableCopy 那么就必須遵守 NSMutableCopying, 并且實(shí)現(xiàn) mutableCopyWithZone: 方法伍纫。

  • 自定義 model,遵守 NSCopyingNSMutableCopying 協(xié)議
@interface CJObj : NSObject<NSCopying, NSMutableCopying>
@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *icon;
@end
  • 實(shí)現(xiàn):


    CJObj.m
  • ViewController 中打印結(jié)果

// 拷貝構(gòu)造
- (void)copyConstruct {
    CJObj *obj = [[CJObj alloc] init];
    obj.name = @"zhangsan";
    obj.icon = @"icon";
    
    NSMutableArray *arr = [NSMutableArray array];
    NSMutableArray *copyAry = [NSMutableArray array];
    [arr addObject:obj];
    
    [copyAry addObject:[arr[0] copy]];
    //[copyAry addObject:[arr[0] mutableCopy]];

    CJObj *obj2 = arr[0];
    obj2.name = @"lisi";
    obj2.icon = @"obj2_icon";
    
    NSLog(@"arr.name = %@, arr.icon = %@", ((CJObj *)arr[0]).name, ((CJObj *)arr[0]).icon);
    NSLog(@"copyArr.name = %@, copyArr.icon = %@", ((CJObj *)[copyAry objectAtIndex:0]).name, ((CJObj *)[copyAry objectAtIndex:0]).icon);
    /* 輸出結(jié)果:
         arr.name = lisi, arr.icon = obj2_icon
         copyArr.name = zhangsan, copyArr.icon = icon
     */
}
  • 內(nèi)存地址圖詳解:(簡單粗糙昂芜,歡迎大神指正)


    拷貝構(gòu)造

以上內(nèi)容在 demo 中均有體現(xiàn)

感謝:

相關(guān)閱讀:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泌神,一起剝皮案震驚了整個濱河市良漱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌欢际,老刑警劉巖母市,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異幼苛,居然都是意外死亡窒篱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門舶沿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來墙杯,“玉大人,你說我怎么就攤上這事括荡「吒洌” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵畸冲,是天一觀的道長嫉髓。 經(jīng)常有香客問我观腊,道長,這世上最難降的妖魔是什么算行? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任梧油,我火速辦了婚禮,結(jié)果婚禮上州邢,老公的妹妹穿的比我還像新娘儡陨。我一直安慰自己,他們只是感情好量淌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布骗村。 她就那樣靜靜地躺著,像睡著了一般呀枢。 火紅的嫁衣襯著肌膚如雪胚股。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天裙秋,我揣著相機(jī)與錄音琅拌,去河邊找鬼。 笑死摘刑,一個胖子當(dāng)著我的面吹牛财忽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泣侮,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼紧唱!你這毒婦竟也來了活尊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤漏益,失蹤者是張志新(化名)和其女友劉穎约急,沒想到半個月后姨夹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年鞋诗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竹海。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡晰骑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出余爆,到底是詐尸還是另有隱情纷宇,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布蛾方,位于F島的核電站像捶,受9級特大地震影響上陕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拓春,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一释簿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧硼莽,春花似錦庶溶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至矾瑰,卻和暖如春砖茸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背殴穴。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工凉夯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人采幌。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓劲够,卻偏偏與公主長得像,于是被迫代替她去往敵國和親休傍。 傳聞我的和親對象是個殘疾皇子征绎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 本文為轉(zhuǎn)載: 作者:zyydeveloper 鏈接:http://www.reibang.com/p/5f776a...
    Buddha_like閱讀 873評論 0 2
  • 307、setValue:forKey和setObject:forKey的區(qū)別是什么磨取? 答:1, setObjec...
    AlanGe閱讀 1,546評論 0 1
  • 前言 不敢說覆蓋OC中所有copy的知識點(diǎn)人柿,但最起碼是目前最全的最新的一篇關(guān)于 copy的技術(shù)文檔了。后續(xù)發(fā)現(xiàn)有新...
    zyydeveloper閱讀 3,358評論 4 35
  • 一 記得是去年冬天忙厌,高升橋路大修的那段時間凫岖,我家門前的那條小路也居然沒了通行能力。且不說每日上班路上的跌宕起伏逢净,就...
    木登閱讀 207評論 0 0
  • 二十天前爹土,我查到了第二次考六級的分?jǐn)?shù):459分(425過線)甥雕,比第一次325整整高了130多分。 但是呢着饥,這個分?jǐn)?shù)...
    靳小凡閱讀 13,129評論 130 614