copy修飾NSArray strong修飾NSMutableArray

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

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


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

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

使用 copy 修飾這個(gè)可變數(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);
}
報(bào)錯(cuò)reason: '-[__NSArrayI removeObject:]: unrecognized selector sent to instance 0x6000000487c0'

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

  • 定義一個(gè)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 時(shí)調(diào)用了 [sAry retain] 方法,實(shí)現(xiàn)了指針拷貝,也就是淺拷貝。
    copy: setter 時(shí)調(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.那么這個(gè)與 copy 修飾 NSMutableArray 導(dǎo)致崩潰有什么關(guān)系呢?

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

  • copy 返回的都是不可變對象,所以如果對 copy 返回值去調(diào)用可變對象的接口就會(huì) crash
  • mutableCopy 返回的都是可變對象
    所以在 - (void)testCash 方法中執(zhí)行到 self.copAry = arr; ARC 環(huán)境下setter 方法執(zhí)行了 copy 方法,導(dǎo)致原本 NSMutableArray類型數(shù)組變成 NSArray 類型,在調(diào)用removeObject:方法時(shí),自然會(huì)出現(xiàn)這個(gè)錯(cuò)誤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
     )
     */
}

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

4.接著再上兩個(gè)測試?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)存地址。
    注意
    多個(gè)對象共用一塊地址時(shí),當(dāng)內(nèi)存銷毀的時(shí)候,指向這片內(nèi)存地址的幾個(gè)指針需要重新定義才可以使用,否則出現(xiàn)野指針現(xiàn)象。
    iOS的淺拷貝 中,通常會(huì)使用 retain 關(guān)鍵字進(jìn)行引用計(jì)數(shù)。因?yàn)樗瓤梢宰寧讉€(gè)指針共同指向同一內(nèi)存地址,也可以在 release 的時(shí)候 由于計(jì)數(shù)的存在,不會(huì)讓內(nèi)存銷毀,從而出現(xiàn)野指針的現(xiàn)象。

深拷貝

  • 深拷貝也就是內(nèi)容拷貝。目標(biāo)對象雖然和原對象的值一樣,但是所指向的內(nèi)存地址不一樣。可以說深拷貝把原對象地址也拷貝了,而內(nèi)存地址是自主分配的。因內(nèi)存地址不一樣,兩個(gè)對象也就互不影響、互不干涉了。
    iOS的深拷貝 中,通常會(huì)使用 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 個(gè)模塊


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

6.拷貝構(gòu)造

當(dāng)然在 iOS 中并不是所有的對象都支持copymutableCopy,遵守 NSCopying 協(xié)議的類可以發(fā)送 copy 消息,遵守NSMutableCopying 協(xié)議的類才可以發(fā)送 mutableCopy 消息。
假如發(fā)送了一個(gè)沒有遵守上訴兩協(xié)議而發(fā)送 copy 或者 mutableCopy, 那么就會(huì)發(fā)生異常。但是默認(rèn)的 iOS 類并沒有遵守這兩個(gè)協(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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,737評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,699評論 2 370

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

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