什么是深拷貝什么是淺拷貝,分別在什么時(shí)候用

根據(jù)拷貝內(nèi)容的不同春叫,分為深淺拷貝

深拷貝:指針賦值,且內(nèi)容拷貝

淺拷貝:只是簡(jiǎn)單的指針賦值

蘋(píng)果為什么這么設(shè)計(jì)呢泣港?總結(jié)起來(lái)很簡(jiǎn)單:即安全又省內(nèi)存暂殖。但是要理解或者避免踩一些坑,還需要看下面的介紹

內(nèi)存

不得不先說(shuō)到內(nèi)存当纱,又不得不說(shuō)內(nèi)存分區(qū):程序底層——程序如何在RAM ROM運(yùn)行呛每,內(nèi)存分配與分區(qū)

看下面圖片:

obj1是定義在函數(shù)外部的全局變量,處于全局區(qū)坡氯;obj2是定義在函數(shù)內(nèi)的局部變量晨横,處于棧區(qū)。它們都指向了處于堆區(qū)的對(duì)象廉沮。

obj1與obj2是指針颓遏,它們指向的對(duì)象是內(nèi)容,那么現(xiàn)在再看深淺拷貝的現(xiàn)象滞时,或者說(shuō)執(zhí)行的結(jié)果:淺拷貝只是多個(gè)指針指向同一對(duì)象內(nèi)容叁幢,深拷貝就是每個(gè)指針都指向了一個(gè)對(duì)象內(nèi)容,互不影響坪稽。

自定義對(duì)象需要自己實(shí)現(xiàn)NSCoping協(xié)議曼玩,一般情況下,自定義對(duì)象都是可變對(duì)象窒百,本節(jié)討論的也都是針對(duì)系統(tǒng)對(duì)象

指針也是會(huì)存在堆區(qū)的黍判,比如在block里面我們知道,如果指針使用了__block修飾篙梢,那么指針會(huì)存放在堆區(qū)顷帖。

返回值的一些基本規(guī)則

無(wú)論是集合對(duì)象還是非集合對(duì)象,在收到copy和mutableCopy消息時(shí),都遵守以下規(guī)則:

1 copy返回immutable對(duì)象贬墩;

2 mutableCopy返回mutable對(duì)象榴嗅;

那么很簡(jiǎn)單,可變與不可變對(duì)象的轉(zhuǎn)變:

不可變對(duì)象→可變對(duì)象的轉(zhuǎn)換:不可變對(duì)象.mutableCopy陶舞。

可變->不可變:可變對(duì)象.copy;

集合拷貝

系統(tǒng)提供的集合類型嗽测,比如字典、數(shù)組肿孵、NSSet等集合類型內(nèi)存基本都是如下結(jié)構(gòu):集合內(nèi)存結(jié)構(gòu)圖

我們可以上面代碼(代碼處于方法內(nèi))做個(gè)分析唠粥,加深對(duì)內(nèi)存的理解。@"123"停做、@"456"是const屬性晤愧,因此處于常量區(qū),指針str1蛉腌、str2养涮、arr局部變量指針處于棧區(qū),@[]數(shù)組內(nèi)容存放位置處于堆區(qū),數(shù)組里面的內(nèi)容存放的是指針str1與str2眉抬,當(dāng)然處于堆區(qū)

其實(shí)arr = @[str1,str2]相當(dāng)于[arr addObject:str1];[arr addObject:str2];,數(shù)組里面有兩個(gè)強(qiáng)指針指向了對(duì)象@"123"與@"456"懈凹。

圖中只是字符串是常量所以在常量區(qū)蜀变,如果他們是NSDate、UIView等等則會(huì)處于堆區(qū)

下面的分析也是基于三種程度的拷貝介评,記為CopyLevel库北,拷貝層次,簡(jiǎn)寫(xiě)CL1们陆、CL2寒瓦、CL3

CL1:arr數(shù)組指針,如果只發(fā)生這層拷貝坪仇,則和非集合對(duì)象一樣杂腰,是淺拷貝

CL2:arr數(shù)組指針指向的的內(nèi)容,即存儲(chǔ)的對(duì)象指針椅文。發(fā)生本層拷貝喂很,從非集合角度來(lái)說(shuō)已經(jīng)發(fā)生了內(nèi)容拷貝,即深拷貝皆刺。但從集合角度來(lái)說(shuō)少辣,還是淺拷貝。

CL3:arr數(shù)組里面存儲(chǔ)的指針指向的內(nèi)容羡蛾,如果發(fā)生本層拷貝漓帅,可以叫做集合的單層深拷貝。

毫無(wú)疑問(wèn),CL1是肯定會(huì)進(jìn)行的忙干。重點(diǎn)就在于CL2于CL3.

不可變集合的copy與mutableCopy

下面代碼器予,不可變集合arrM1的copy與mutableCopy。arrM2:mutableCopy豪直,arr:copy

根據(jù)第一行打印結(jié)果:arrM2和arr都進(jìn)行CL1拷貝

第二行打印結(jié)果:arrM2與arrM1結(jié)果不同劣摇,說(shuō)明進(jìn)行了數(shù)組拷貝;arr與arrM1結(jié)果相同弓乙,說(shuō)明沒(méi)有末融,進(jìn)行數(shù)組拷貝

第三行打印結(jié)果:都相同,說(shuō)明指向的內(nèi)容沒(méi)有發(fā)生拷貝

可變集合的copy與mutableCopy

下面代碼暇韧,可變集合arrM1的copy與mutableCopy勾习。arrM2:mutableCopy,arr:copy

根據(jù)第一行打印結(jié)果:arrM2和arr都進(jìn)行CL1拷貝

第二行打印結(jié)果:結(jié)果均不同懈玻,說(shuō)明都進(jìn)行了數(shù)組拷貝巧婶;

第三行打印結(jié)果:都相同,說(shuō)明指向的內(nèi)容沒(méi)有發(fā)生拷貝

一般結(jié)論

我們知道涂乌,對(duì)于非集合對(duì)象艺栈,有如下結(jié)論:

// 不可變,線程安全

[immutableObject copy] // 淺復(fù)制

[immutableObject mutableCopy] // 深復(fù)制湾盒,對(duì)于集合則是只拷?貝數(shù)組的內(nèi)容湿右,數(shù)組的內(nèi)容是指針,而指針的內(nèi)容不會(huì)被拷?

// 可變對(duì)象罚勾,線程不安全

[mutableObject copy] //深復(fù)制毅人,對(duì)于集合則是只拷?貝數(shù)組的內(nèi)容,數(shù)組的內(nèi)容是指針尖殃,而指針的內(nèi)容不會(huì)被拷?

[mutableObject mutableCopy] //深復(fù)制丈莺,對(duì)于集合則是只拷?貝數(shù)組的內(nèi)容,數(shù)組的內(nèi)容是指針送丰,而指針的內(nèi)容不會(huì)被拷?

集合的單層深拷貝缔俄,CL3層的拷貝(one-level-deep copy)

我們需要使用- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag;方法,且flag為YES蚪战。

可以看到牵现,三行打印結(jié)果都不一樣,即發(fā)生了CL3層的拷貝邀桑。

此方法執(zhí)行后瞎疼,arrM1集合里的每個(gè)對(duì)象都會(huì)收到 copyWithZone: 消息。如果集合里的對(duì)象遵循 NSCopying 協(xié)議壁畸,那么對(duì)象就會(huì)被深拷貝到新的集合贼急,如果沒(méi)有遵循就直接崩潰了茅茂。

等一等,好像有另一個(gè)問(wèn)題:此方法只是會(huì)給集合的每個(gè)對(duì)象發(fā)送copyWithZone:方法太抓,那么對(duì)于不可變對(duì)象空闲,copyWithZone:的執(zhí)行還是淺拷貝。讀者大概也注意到了走敌,圖中示例代碼碴倾,arrM1數(shù)組存的也是可變對(duì)象dict1,所以有CL3層的拷貝掉丽。那如果arrM1存的不是可變對(duì)象呢跌榔?結(jié)果就是沒(méi)有CL3層的拷貝,大家可以用代碼測(cè)試下捶障!

為啥叫單層深復(fù)制呢僧须? 因?yàn)樗唤oarrM1數(shù)組存的對(duì)象發(fā)送了copyWithZone:方法,而沒(méi)有對(duì)dict1發(fā)送copyWithZone:方法项炼,dict1也是集合担平,它里面也存放著對(duì)象呢。锭部。暂论。即集合里面存放的集合。拌禾。空另。好繞,哈哈

另外蹋砚,除了此方法,集合的解檔歸檔摄杂,也是可以實(shí)現(xiàn)單層深拷貝的坝咐。

繞的東西就到這里,下面看些感興趣的東西:

一些坑

Mutable變copy的坑

有一點(diǎn)需要注意了:copy返回值為不可變對(duì)象析恢,如果使用可變對(duì)象的接口就會(huì)crash墨坚。例如:

- (void)arrMCopyTest {

NSMutableArray *arrM = [NSMutableArray arrayWithObjects:@"123",@"456", nil];

NSMutableArray *arr = [arrM copy];

// 下面代碼崩潰

[arr addObject:@"789"];

}

[arrM copy];返回的是不可變類型,即NSArray映挂,向一個(gè)NSArray對(duì)象發(fā)送addObject消息當(dāng)然方法找不到崩潰泽篮。

另一個(gè)問(wèn)題,arr是NSMutableArray類型柑船,它指向父類NSArray編譯器為什么不報(bào)錯(cuò)呢帽撑?copy返回的是id類型,編譯器不會(huì)對(duì)id(俗稱萬(wàn)能指針)進(jìn)行類型檢查鞍时,所以會(huì)經(jīng)晨骼看到推薦使用instancetype扣蜻,而不是id

下面的類似錯(cuò)誤就很常見(jiàn)了:

@property (nonatomic, copy) NSMutableArray *arr;

- (void)arrMCopyTest {

NSMutableArray *arrM = [NSMutableArray arrayWithObjects:@"123",@"456", nil];

self.arr = arrM;

// 下面代碼崩潰

[self.arr addObject:@"789"];

}

因?yàn)閟elf.arr為copy修飾备典,那么self.arr = arrM就相當(dāng)于_arr = [arrM copy]

屬性指定為copy冬三,卻沒(méi)有被copy

@property (nonatomic, copy) NSString *str;

- (void)viewDidLoad {

NSMutableString *str = [NSMutableString stringWithFormat:@"123"];

// self.str = str;

_str = str;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

[str appendString:@"456"];

NSLog(@"change");

});

}

這里在block里面對(duì)str進(jìn)行操作,居然沒(méi)有對(duì)它進(jìn)行__block修飾1烀s狭拧芳肌!感興趣可以看看這篇博客:iOS中block的使用、實(shí)現(xiàn)底層肋层、循環(huán)引用亿笤、存儲(chǔ)位置

打印結(jié)果:

2017-07-23 00:33:06.344 CopyTest[95611:31912803] 123

2017-07-23 00:33:07.518 CopyTest[95611:31912803] change

2017-07-23 00:33:08.636 CopyTest[95611:31912803] 123456

都是123456,self.str被意外改變了槽驶,如果將代碼_str = str;-->self.str = str;值就不會(huì)改變了责嚷。因?yàn)橄喈?dāng)于_str = [str copy];。

所以建議除了在初始化時(shí)(init方法中)掂铐,蘋(píng)果推薦我們使用_下劃線的方式直接訪問(wèn)變量罕拂,其它地方盡量使用self.來(lái)訪問(wèn)。另外我們還經(jīng)常getter或者setter方法里面做一些自定義操作全陨,如果_方式則這些自定義操作就不會(huì)被執(zhí)行爆班。而且在block里面使用_方式訪問(wèn)變量會(huì)更隱蔽的引起循環(huán)引用的問(wèn)題!

setter方法

@property (nonatomic, copy) NSString *str;

- (void)setStr:(NSString *)str {

// _str = str; 不要這樣寫(xiě)

_str = [str copy];

}

講了這些辱姨,大家會(huì)不會(huì)猛然想到

@property (nonatomic, weak) id delegate;

_delegate = obj;

這樣會(huì)不會(huì)造成_delegate為指向的對(duì)象引用計(jì)數(shù)為0時(shí)柿菩,系統(tǒng)還會(huì)不會(huì)將_delegate置為nil?答案是雨涛,您多慮了枢舶,會(huì)的。這和copy不一樣替久。為啥不一樣凉泄?牽涉到runtime哈希表什么的就不在展開(kāi)了。蚯根。后众。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市颅拦,隨后出現(xiàn)的幾起案子蒂誉,更是在濱河造成了極大的恐慌,老刑警劉巖距帅,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件右锨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡碌秸,警方通過(guò)查閱死者的電腦和手機(jī)陡蝇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)痊臭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人登夫,你說(shuō)我怎么就攤上這事广匙。” “怎么了恼策?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵鸦致,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我涣楷,道長(zhǎng)分唾,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任狮斗,我火速辦了婚禮绽乔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碳褒。我一直安慰自己折砸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布沙峻。 她就那樣靜靜地躺著睦授,像睡著了一般。 火紅的嫁衣襯著肌膚如雪摔寨。 梳的紋絲不亂的頭發(fā)上去枷,一...
    開(kāi)封第一講書(shū)人閱讀 51,258評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音是复,去河邊找鬼删顶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛淑廊,可吹牛的內(nèi)容都是我干的翼闹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蒋纬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了坚弱?” 一聲冷哼從身側(cè)響起蜀备,我...
    開(kāi)封第一講書(shū)人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎荒叶,沒(méi)想到半個(gè)月后碾阁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡些楣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年脂凶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宪睹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚕钦,死狀恐怖亭病,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嘶居,我是刑警寧澤罪帖,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站邮屁,受9級(jí)特大地震影響整袁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜佑吝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一坐昙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芋忿,春花似錦炸客、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至逆趣,卻和暖如春蝶溶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宣渗。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工抖所, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人痕囱。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓田轧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親鞍恢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子傻粘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

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

  • 1、對(duì)象拷貝有兩種方式:淺復(fù)制和深復(fù)制帮掉。顧名思義弦悉,淺復(fù)制,并不拷貝對(duì)象本身蟆炊,僅僅是拷貝指向?qū)ο蟮闹羔樆颍簧顝?fù)制是直接...
    滴答大閱讀 770評(píng)論 0 2
  • [TOC] 引言 根據(jù)拷貝內(nèi)容的不同,分為深淺拷貝 深拷貝:內(nèi)容拷貝涩搓,且將指針指向新的內(nèi)容 淺拷貝:只是簡(jiǎn)單的指針...
    時(shí)間已靜止閱讀 1,692評(píng)論 1 14
  • 1.設(shè)計(jì)模式是什么污秆? 你知道哪些設(shè)計(jì)模式劈猪,并簡(jiǎn)要敘述? 設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn)良拼,就是用比較成熟的邏輯去處理某一種類...
    司馬DE晴空閱讀 1,291評(píng)論 0 7
  • 設(shè)計(jì)模式是什么战得? 你知道哪些設(shè)計(jì)模式,并簡(jiǎn)要敘述将饺? 設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn)贡避,就是用比較成熟的邏輯去處理某一種類型的...
    iOS菜鳥(niǎo)大大閱讀 707評(píng)論 0 1
  • 概念 拷貝的方式有兩種:淺拷貝(Shallow Copy)和深拷貝(Deep Copy)。 從字面意思理解予弧,淺拷貝...
    kscorpio閱讀 898評(píng)論 0 5