關(guān)于iOS中深淺拷貝的理解

在iOS開(kāi)發(fā)過(guò)程中据某,我們要對(duì)某個(gè)對(duì)象進(jìn)行拷貝的時(shí)候墩朦,一般會(huì)用copymutableCopy兩種方法勺阐,在剛接觸iOS的時(shí)候,我一般把copy稱為淺拷貝幢痘,mutableCopy稱為深拷貝唬格,但是隨著不斷踩坑家破,才發(fā)現(xiàn)對(duì)不同類型的對(duì)象執(zhí)行拷貝操作颜说,得到的結(jié)果跟自己的預(yù)期并不一致购岗。然后看了網(wǎng)上諸多關(guān)于深淺拷貝的文章,再結(jié)合自己寫(xiě)代碼測(cè)試门粪,才慢慢理解到喊积,其實(shí)iOS中的深淺拷貝得到的結(jié)果,是跟拷貝對(duì)象的類型有關(guān)系的玄妈。

在iOS開(kāi)發(fā)中乾吻,一般情況下,拷貝對(duì)象大概分為3種:非集合對(duì)象拟蜻、集合對(duì)象绎签、自定義對(duì)象。好了酝锅,話不多講诡必,以下就是針對(duì)這三種不同類型對(duì)象所寫(xiě)的深淺拷貝測(cè)試代碼。

1搔扁、iOS中非集合類的深淺拷貝

非集合類對(duì)象深淺拷貝示例代碼:

- (void)stringCopy{
    NSString *str = @"123456";
    // 內(nèi)容內(nèi)存地址不變爸舒,共用同一塊內(nèi)存
    NSString *cpyStr = [str copy];
    // 內(nèi)容地址改變,另外開(kāi)辟一塊內(nèi)存空間稿蹲,內(nèi)容可變
    NSMutableString *mutCpyStr = [str mutableCopy];
    // str = 0x10e6d9068; copyStr = 0x10e6d9068; mutStr = 0x604000253f20;
    NSLog(@"NSString內(nèi)容地址:str = %p; copyStr = %p; mutStr = %p;",str,cpyStr,mutCpyStr);
    // str = 0x7fff51525be8; copyStr = 0x7fff51525be0; mutStr = 0x7fff51525bd8;
    NSLog(@"NSstring指針地址:str = %p; copyStr = %p; mutStr = %p;",&str,&cpyStr,&mutCpyStr);
}
- (void)mutableStringCopy{
    NSMutableString *formatStr = [NSMutableString stringWithFormat:@"呵呵噠??"];
    // 內(nèi)容地址改變扭勉,開(kāi)辟一塊內(nèi)存空間,內(nèi)容不可變
    NSString *cpyStr = [formatStr copy];
    // 內(nèi)容地址改變苛聘,開(kāi)辟一塊內(nèi)存空間涂炎,內(nèi)容可變
    NSMutableString *mutCpyStr = [formatStr mutableCopy];
    // str = 0x600000249690; copyStr = 0x600000249cc0; mutStr = 0x600000249750;
    NSLog(@"NSMutableString內(nèi)容地址:str = %p; copyStr = %p; mutStr = %p;",formatStr,cpyStr,mutCpyStr);
    // str = 0x7fff51525be8; copyStr = 0x7fff51525be0; mutStr = 0x7fff51525bd8;
    NSLog(@"NSMutableString指針地址:str = %p; copyStr = %p; mutStr = %p;",&formatStr,&cpyStr,&mutCpyStr);
}

由上面的代碼可以看出,非集合對(duì)象的拷貝規(guī)律是這樣的:

  • 不可變的非集合對(duì)象copy设哗,指針拷貝璧尸,共用同一片內(nèi)存;
  • 不可變的非集合對(duì)象mutableCopy熬拒,創(chuàng)建新內(nèi)存空間爷光,成為可變對(duì)象;
  • 可變的非集合對(duì)象copy澎粟,創(chuàng)建新內(nèi)存空間蛀序,成為不可變對(duì)象;
  • 可變的非集合對(duì)象mutableCopy活烙,創(chuàng)建新內(nèi)存空間徐裸。

2、iOS中集合類的深淺拷貝

集合類的拷貝主要分為下面三種類型:

  • 淺復(fù)制(shallow copy):在淺復(fù)制操作時(shí)啸盏,對(duì)于被復(fù)制對(duì)象的每一層都是指針復(fù)制重贺。
  • 深復(fù)制(one-level-deep copy):在深復(fù)制操作時(shí),對(duì)于被復(fù)制對(duì)象,至少有一層是深復(fù)制气笙。
  • 完全復(fù)制(real-deep copy):在完全復(fù)制操作時(shí)次企,對(duì)于被復(fù)制對(duì)象的每一層都是對(duì)象復(fù)制。

來(lái)自蘋(píng)果官方的解釋圖例:

3.jpg

集合的深淺拷貝示例代碼:

- (void)collectionCopy{
    NSString *str = [NSString stringWithFormat:@"呵呵噠"];
    NSMutableString *mutStr = [NSMutableString stringWithFormat:@"萌萌噠"];
    NSArray *arr = [NSArray arrayWithObjects:@"one", @"two", @"three", nil];
    // 不可變集合對(duì)象只包含非集合對(duì)象
    NSArray *imutArr1 = [NSArray arrayWithObjects:str, mutStr, nil];
    // 淺復(fù)制潜圃,創(chuàng)建一個(gè)指針直接指向已有內(nèi)存空間
    NSArray *cpyImutArr1 = [imutArr1 copy];
    // 深復(fù)制缸棵,創(chuàng)建新的集合內(nèi)存空間,新瓶裝老酒
    NSMutableArray *mutCpyImutArr1 = [imutArr1 mutableCopy];
    // 創(chuàng)建新的集合內(nèi)存空間谭期,并且對(duì)集合內(nèi)元素進(jìn)行 copy 操作
    NSMutableArray *initArr1 = [[NSMutableArray alloc] initWithArray:imutArr1 copyItems:YES];
    // 完全深復(fù)制堵第,如果元素是自定義類型,需要實(shí)現(xiàn)NSCoding協(xié)議
    NSArray *deepCpyArr1 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:imutArr1]];
        NSLog(@"\n imutArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&imutArr1, imutArr1, imutArr1[0], imutArr1[1], imutArr1[2]);
    // imutArr1 info : pointer = 0x7fff5d946bc8; content = 0x604000443960; first = 0x604000037520; second = 0x604000442d30; third = 0x6040004446e0
    NSLog(@"\n cpyImutArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&cpyImutArr1, cpyImutArr1, cpyImutArr1[0], cpyImutArr1[1], cpyImutArr1[2]);
    // cpyImutArr1 info : pointer = 0x7fff5d946bc0; content = 0x604000443960; first = 0x604000037520; second = 0x604000442d30; third = 0x6040004446e0
    NSLog(@"\n mutCpyImutArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&mutCpyImutArr1, mutCpyImutArr1, mutCpyImutArr1[0], mutCpyImutArr1[1], mutCpyImutArr1[2]);
    // mutCpyImutArr1 info : pointer = 0x7fff5d946bb8; content = 0x604000444290; first = 0x604000037520; second = 0x604000442d30; third = 0x6040004446e0
    NSLog(@"\n initArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&initArr1, initArr1, initArr1[0], initArr1[1], initArr1[2]);
    // initArr1 info : pointer = 0x7fff5d946bb0; content = 0x604000442e80; first = 0x604000037520; second = 0x604000231aa0; third = 0x6040004446e0
    NSLog(@"\n deepCpyArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&deepCpyArr1, deepCpyArr1, deepCpyArr1[0], deepCpyArr1[1], deepCpyArr1[2]);
    // deepCpyArr1 info : pointer = 0x7fff5d946ba8; content = 0x604000443210; first = 0x604000231ce0; second = 0x604000444710; third = 0x6040004441d0
    // 可變集合對(duì)象包含集合對(duì)象
    NSMutableArray *mutArr2 = [NSMutableArray arrayWithObjects:str, mutStr, arr, nil];
    // 深復(fù)制隧出,創(chuàng)建新的集合內(nèi)存空間踏志,內(nèi)容不變,新的集合不可變
    NSMutableArray *cpyMutArr2 = [mutArr2 copy];
    // 深復(fù)制胀瞪,創(chuàng)建新的集合內(nèi)存空間狰贯,內(nèi)容不變
    NSMutableArray *mutCpyMutArr2 = [mutArr2 mutableCopy];
    NSLog(@"\n mutArr2 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&mutArr2, mutArr2, mutArr2[0], mutArr2[1], mutArr2[2]);
    // mutArr2 info : pointer = 0x7fff53507bc8; content = 0x60400025e2a0; first = 0x60400023fa40; second = 0x60400025d2b0; third = 0x60400025f710
    NSLog(@"\n cpyMutArr2 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&cpyMutArr2, cpyMutArr2, cpyMutArr2[0], cpyMutArr2[1], cpyMutArr2[2]);
    // cpyMutArr2 info : pointer = 0x7fff53507bc0; content = 0x60400025d760; first = 0x60400023fa40; second = 0x60400025d2b0; third = 0x60400025f710
    NSLog(@"\n mutCpyImutArr1 info : pointer = %p; content = %p; first = %p; second = %p; third = %p",&mutCpyMutArr2, mutCpyMutArr2, mutCpyMutArr2[0], mutCpyMutArr2[1], mutCpyMutArr2[2]);
    // mutCpyImutArr1 info : pointer = 0x7fff53507bb8; content = 0x60400025d310; first = 0x60400023fa40; second = 0x60400025d2b0; third = 0x60400025f710
}

通過(guò)代碼和日志可以看出:

  • 不可變集合的拷貝規(guī)律大概是這樣的:
    [immutableObject copy] // 淺復(fù)制,指針復(fù)制
    [immutableObject mutableCopy] //深復(fù)制赏廓,不是完全復(fù)制涵紊,容器是新的可變?nèi)萜鳎瑑?nèi)容還是舊的

  • 可變集合的拷貝規(guī)律大概是這樣的:
    [mutableObject copy] //深復(fù)制 返回對(duì)象為immutable
    [mutableObject mutableCopy] //深復(fù)制幔摸,不是完全復(fù)制摸柄,容器是新的可變?nèi)萜鳎瑑?nèi)容還是舊的

若想要實(shí)現(xiàn)完全拷貝既忆,則需要使用下面的代碼:

NSArray *newArr = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArr]];

至于拷貝后的集合是否可變驱负,則跟舊集合的是否可變一致。值得注意的是患雇,如果集合內(nèi)含有自定義的類對(duì)象跃脊,則該類需要實(shí)現(xiàn)NSCoding協(xié)議。

如果根據(jù)集合重新init一個(gè)新的集合出來(lái)苛吱,就像這樣子:

NSMutableArray *newArr = [[NSMutableArray alloc] initWithArray:arr copyItems:YES];

那么生成的新集合中的元素酪术,并不一定都是完全拷貝的。因?yàn)閳?zhí)行上面的代碼翠储,只會(huì)對(duì)集合內(nèi)的元素執(zhí)行copy方法绘雁。

3、iOS中自定義對(duì)象的深淺拷貝

iOS中自定義對(duì)象要想實(shí)現(xiàn)拷貝援所,必須要實(shí)現(xiàn)NSCopying或者NSMutableCopying協(xié)議庐舟。
自定義對(duì)象實(shí)現(xiàn)拷貝示例代碼:

// Cat.h
#import <Foundation/Foundation.h>
@interface Cat : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic, assign) NSUInteger age;
@property (nonatomic, copy) NSString *name;
@end
// Cat.m
#import "Cat.h"
@implementation Cat
- (instancetype)copyWithZone:(NSZone *)zone{
    return self;
}
- (instancetype)mutableCopyWithZone:(NSZone *)zone{
    Cat *cat = [[Cat alloc] init];
    cat.name = @"hello";
    return cat;
}
@end

從代碼可以看出,自定義類要遵守NSCopy協(xié)議或者NSMutableCopy協(xié)議住拭,拷貝協(xié)議的代碼由自己來(lái)實(shí)現(xiàn)挪略,那么自定義類對(duì)象執(zhí)行Copy或者mutableCopy所得的結(jié)果历帚,無(wú)論是指針層面的淺拷貝,還是完全拷貝杠娱,都是由自己來(lái)決定的挽牢。值得注意的是,如果集合里面包含了自定義類對(duì)象墨辛,那么要用NSKeyedArchiver/NSKeyedUnarchiver實(shí)現(xiàn)集合的完全拷貝,則需要自定義類實(shí)現(xiàn)NSCoding協(xié)議趴俘。

- (id)initWithCoder:(NSCoder *)aDecoder{
    self = [super init];
    if (self == nil){
        return nil;
    }
    self.name = [aDecoder decodeObjectForKey:@"name"];
    // 依次寫(xiě)下想要decode的屬性
    return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:self.name forKey:@"name"];
    // 依次寫(xiě)下想要encode的屬性
}

(此文完結(jié))

作者:薛小全睹簇,開(kāi)發(fā)者,擅長(zhǎng)Objective-C,HTML,CSS,JavaScript寥闪。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末太惠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子疲憋,更是在濱河造成了極大的恐慌凿渊,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缚柳,死亡現(xiàn)場(chǎng)離奇詭異埃脏,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)秋忙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)彩掐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人灰追,你說(shuō)我怎么就攤上這事堵幽。” “怎么了弹澎?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵朴下,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我苦蒿,道長(zhǎng)殴胧,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任佩迟,我火速辦了婚禮溃肪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘音五。我一直安慰自己惫撰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布躺涝。 她就那樣靜靜地躺著厨钻,像睡著了一般扼雏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上夯膀,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天诗充,我揣著相機(jī)與錄音,去河邊找鬼诱建。 笑死蝴蜓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俺猿。 我是一名探鬼主播茎匠,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼押袍!你這毒婦竟也來(lái)了诵冒?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤谊惭,失蹤者是張志新(化名)和其女友劉穎汽馋,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體圈盔,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡豹芯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驱敲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片告组。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖癌佩,靈堂內(nèi)的尸體忽然破棺而出木缝,到底是詐尸還是另有隱情,我是刑警寧澤围辙,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布我碟,位于F島的核電站,受9級(jí)特大地震影響姚建,放射性物質(zhì)發(fā)生泄漏矫俺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一掸冤、第九天 我趴在偏房一處隱蔽的房頂上張望厘托。 院中可真熱鬧,春花似錦稿湿、人聲如沸铅匹。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)包斑。三九已至流礁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間罗丰,已是汗流浹背神帅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留萌抵,地道東北人找御。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像绍填,于是被迫代替她去往敵國(guó)和親霎桅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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