我理解的深拷貝和淺拷貝

版本 時間
V1.0 2019-01-20

前言

今天一個朋友跟我討論一下這個深拷貝淺拷貝的話題,然后感覺自己說的也不是很清楚,所以又特地找了一些資料城丧,讓自己再了解學習一下。

正文

概念

淺拷貝

淺拷貝指的是復制引用對象的指針豌鹤,而不是復制引用對象本身亡哄,拷貝出來的對象指針和引用對象的指針指向同一塊內存地址,當這塊內存地址銷毀的時候傍药,指向這塊內存地址的所有指針也需要重新定義磺平,不然就會造成野指針錯誤魂仍。

深拷貝

深拷貝指的是復制引用對象的內容拐辽,也就是說內容的所占有的內存地址需要重新分配拣挪,復制完之后,內存中的值是完全相同的俱诸,但是內存地址是不一樣的菠劝,拷貝得到的對象和引用對象之間是互不影響,也互不干擾睁搭。

應用場景

淺拷貝場景

1.使用retain操作赶诊,返回的對象是否可變與被拷貝對象保持一致,在iOS中园骆,使用retain關鍵字進行引用計數舔痪,這是一種更加保險的淺拷貝;
2.對于不可變對象進行copy操作的是淺拷貝,引用計數每次+1;

深拷貝場景

1.對可變對象進行copy操作锌唾,引用計數不改變
2.使用mutableCopy操作的始終是深拷貝锄码,引用計數不改變,返回的是一個可變對象晌涕。

代碼驗證

代碼一

NSArray *list = [[NSArray alloc] initWithObjects:@"1",@"2",@"3",@"4", nil];
NSLog(@"\n被拷貝對象的內存地址:%p",list);
NSLog(@"\n====被拷貝對象的引用計數:%ld",CFGetRetainCount((__bridge CFTypeRef)list));

NSArray *listCopy = [list copy];
NSLog(@"\n拷貝對象的內存地址:%p",listCopy);
NSLog(@"\n====被拷貝對象的引用計數:%ld",CFGetRetainCount((__bridge CFTypeRef)list));

NSMutableArray *listMutableCopy = [list copy];
NSLog(@"\n拷貝對象的內存地址:%p",listMutableCopy);
NSLog(@"\n====被拷貝對象的引用計數:%ld",CFGetRetainCount((__bridge CFTypeRef)list));

NSMutableArray *mutablelistMutableCopy = [list mutableCopy];
NSLog(@"\n拷貝對象的內存地址:%p",mutablelistMutableCopy);
NSLog(@"\n====被拷貝對象的引用計數:%ld",CFGetRetainCount((__bridge CFTypeRef)list));

打印結果:
被拷貝對象的內存地址:0x600000aca370
====被拷貝對象的引用計數:1
拷貝對象的內存地址:0x600000aca370
====被拷貝對象的引用計數:2
拷貝對象的內存地址:0x600000aca370
====被拷貝對象的引用計數:3
拷貝對象的內存地址:0x600000ad75d0
====被拷貝對象的引用計數:3

由此可見對NSArray進行copy操作滋捶,返回的地址是一樣的,每次進行copy操作時余黎,被拷貝對象的引用計數都會+1重窟,是淺拷貝,而對NSArray進行mutableCopy操作惧财,返回的地址是不一樣的巡扇,被拷貝對象的引用計數沒有發(fā)生變化,是深拷貝

代碼二

NSMutableArray *list = [[NSMutableArray alloc] initWithObjects:@"1",@"2",@"3",@"4", nil];
NSLog(@"\n被拷貝對象的內存地址:%p",list);
NSLog(@"\n====被拷貝對象的引用計數:%ld",CFGetRetainCount((__bridge CFTypeRef)list));

NSArray *listCopy = [list copy];
NSLog(@"\n拷貝對象的內存地址:%p",listCopy);
NSLog(@"\n====被拷貝對象的引用計數:%ld",CFGetRetainCount((__bridge CFTypeRef)list));

NSMutableArray *listMutableCopy = [list copy];
NSLog(@"\n拷貝對象的內存地址:%p",listMutableCopy);
NSLog(@"\n====被拷貝對象的引用計數:%ld",CFGetRetainCount((__bridge CFTypeRef)list));

NSMutableArray *mutablelistMutableCopy = [list mutableCopy];
NSLog(@"\n拷貝對象的內存地址:%p",mutablelistMutableCopy);
NSLog(@"\n====被拷貝對象的引用計數:%ld",CFGetRetainCount((__bridge CFTypeRef)list));

打印結果:
被拷貝對象的內存地址:0x600002d7d1d0
====被拷貝對象的引用計數:1
拷貝對象的內存地址:0x600002d78600
====被拷貝對象的引用計數:1
拷貝對象的內存地址:0x600002d786c0
====被拷貝對象的引用計數:1
拷貝對象的內存地址:0x600002d78690
====被拷貝對象的引用計數:1

由此可見對NSMutableArray無論是進行copy還是mutableCopy操作垮衷,返回的地址都是不一樣的厅翔,都新開辟了新的內存空間,并且被拷貝對象的引用計數不變帘靡,屬于是深拷貝知给。

得到的結論:不可變對象進行copy,是淺拷貝描姚,該對象引用計數會+1;對可變對象進行無論是進行copy或者mutableCopy涩赢,則是深拷貝,引用計數不變轩勘。

深拷貝筒扒、淺拷貝的實現

如果需要實現自定義對象的拷貝,那么該自定義對象的類必須實現NSCopying或者NSMutableCopy協(xié)議绊寻,并且實現copyWithZone或者mutableCopyWithZone方法花墩。

主要用到的兩個方法:

- (id)copyWithZone:(nullable NSZone *)zone;

- (id)mutableCopyWithZone:(nullable NSZone *)zone;

注意:如果一個自定義對象中需要實現NSCopying或者NSMutableCopy的話悬秉,那么這個自定義對象中如果包含其他的自定義對象的話,那么其他的自定義對象也需要實現copy或者mutableCopy協(xié)議冰蘑。

面試相關

野指針

野指針表示的是不為NULL指針和泌,指向不可用內存的指針(也就是說內存已經被釋放或者壓根不存在的指針)。

野指針常見定位方式:

1.通過多次重現問題祠肥,進行近一步的有效信息武氓,獲取發(fā)生問題的大概位置;

2.通過Xcode提供的Malloc Scribble對已釋放內存進行數據填充仇箱,從而保證野指針訪問必然Crash县恕;

3.通過Zombie Objects將已釋放的對象標記成Zombie對象,再次對該對象發(fā)送消息時剂桥,會發(fā)生Crash并且輸出相關的調用信息忠烛。

copymutableCopyretain的關系

copy對于可變對象來說是深拷貝权逗,引用計數不會變化美尸,而對于不可變對象來說是淺拷貝,引用計數每次都會+1旬迹。

mutableCopy始終是深拷貝火惊,引用計數不會發(fā)生變換,始終返回的是一個可變對象奔垦。

retain始終是淺拷貝,引用計數每次都會+1屹耐。

block為什么需要使用copy來修飾

block通過存儲方式可以分為棧區(qū)block,堆區(qū)block,全局blockblockMRC環(huán)境下常用copy來修飾椿猎,在MRC中惶岭,block的內存地址顯示在棧區(qū),棧區(qū)的特點是創(chuàng)建的對象隨時可能會被系統(tǒng)銷毀犯眠,一旦被銷毀后再次調用按灶,可能會造成程序Crash,對block進行copy后,block會被存放在堆區(qū)筐咧,存放在堆上的block,也就有了引用計數鸯旁,后續(xù)的復制操作都不會真的執(zhí)行復制,而是增加block對象的引用計數量蕊,這樣block就不會被系統(tǒng)銷毀铺罢,而是需要開發(fā)者釋放了(在MRC中引用計數需要手動管理,在ARC中引用計數可以通過系統(tǒng)管理)残炮。在ARCBlock都會在堆區(qū)韭赘,系統(tǒng)會默認對block進行copy操作。

Stringcopystrong的區(qū)別

字符串用copy是將字符串本身拷貝一份势就,在內存中存在兩個字符串泉瞻,但是內存地址不一樣;
字符串用strong是將字符串的指針拷貝一份脉漏,在內存中只有一個字符串,且內存地址是一樣的;
如果需要修改字符串類容的話袖牙,那么需要使用strong來聲明侧巨,用copy來修飾的字符串不會有變化;
開發(fā)過程中如果使用的字符串是獨立的話,建議使用copy贼陶,內存中會copy一個新的內容出來刃泡,不會收到其他賦值的改變巧娱。

總結

深拷貝淺拷貝的本質區(qū)別就是內存地址是否相同碉怔。

淺拷貝的引用對象和拷貝得到的對象就好像是一個人和他的影子,這個人掛了禁添,他的影子也就沒了
深拷貝的引用對象和拷貝得到的對象就好像是一個人和他的克隆人撮胧,這個人掛了,但是克隆人還是可以活著的老翘。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末芹啥,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子铺峭,更是在濱河造成了極大的恐慌墓怀,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卫键,死亡現場離奇詭異傀履,居然都是意外死亡,警方通過查閱死者的電腦和手機莉炉,發(fā)現死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進店門钓账,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人絮宁,你說我怎么就攤上這事梆暮。” “怎么了绍昂?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵啦粹,是天一觀的道長。 經常有香客問我窘游,道長唠椭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任张峰,我火速辦了婚禮泪蔫,結果婚禮上,老公的妹妹穿的比我還像新娘喘批。我一直安慰自己撩荣,他們只是感情好铣揉,可當我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著餐曹,像睡著了一般逛拱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上台猴,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天朽合,我揣著相機與錄音,去河邊找鬼饱狂。 笑死曹步,一個胖子當著我的面吹牛,可吹牛的內容都是我干的休讳。 我是一名探鬼主播讲婚,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼俊柔!你這毒婦竟也來了筹麸?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤雏婶,失蹤者是張志新(化名)和其女友劉穎物赶,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體留晚,經...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡酵紫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了倔丈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憨闰。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖需五,靈堂內的尸體忽然破棺而出鹉动,到底是詐尸還是另有隱情,我是刑警寧澤宏邮,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布泽示,位于F島的核電站,受9級特大地震影響蜜氨,放射性物質發(fā)生泄漏械筛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一飒炎、第九天 我趴在偏房一處隱蔽的房頂上張望埋哟。 院中可真熱鬧,春花似錦、人聲如沸赤赊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抛计。三九已至哄孤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吹截,已是汗流浹背瘦陈。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留波俄,地道東北人晨逝。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像弟断,于是被迫代替她去往敵國和親咏花。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,566評論 2 349

推薦閱讀更多精彩內容