在iOS開(kāi)發(fā)中深拷貝和淺拷貝是一個(gè)被大家說(shuō)爛的話題了疯特,但是今天還是要拿出來(lái)說(shuō)一說(shuō)淹办。原因是印荔,前段時(shí)間在微信朋友圈看到一個(gè)朋友發(fā)的關(guān)于深拷貝和淺拷貝的總結(jié)攻询,當(dāng)時(shí)看了一眼从撼,我想他對(duì)深拷貝和淺拷貝沒(méi)有理解,而且被網(wǎng)上很多關(guān)于這方面的技術(shù)博客誤導(dǎo)了钧栖,我搜索了一下低零,發(fā)現(xiàn)網(wǎng)上很多的博客跟他下面總結(jié)的一樣。而且他去面試時(shí)拯杠,很多面試官也是這么理解深拷貝和淺拷貝的掏婶。
他的總結(jié)如下圖,大家可以看看潭陪,想想自己是否也是這么理解深拷貝和淺拷貝的:
下面讓我們先來(lái)看幾個(gè)個(gè)例子雄妥,如果你認(rèn)可上圖的總結(jié)最蕾,那么看完下面的例子之后,請(qǐng)先想好結(jié)果老厌,然后再對(duì)照答案瘟则,看看自己的結(jié)果是否正確。
1. NSMutableArray對(duì)象的拷貝
我們先來(lái)看看對(duì)可變數(shù)組NSMutableArray
對(duì)象進(jìn)行拷貝的例子:
//1.創(chuàng)建四個(gè)可變字符串
NSMutableString *av_0 = [NSMutableString stringWithString:@"Harry"];
NSMutableString *av_1 = [NSMutableString stringWithString:@"Jamie"];
NSMutableString *av_2 = [NSMutableString stringWithString:@"Micheal"];
NSMutableString *av_3 = [NSMutableString stringWithString:@"Susan"];
//2.創(chuàng)建一個(gè)可變數(shù)組枝秤,用來(lái)存放上面四個(gè)可變的字符串
NSMutableArray *array_0 = [NSMutableArray arrayWithObjects:av_0,av_1, nil];
//3.我們調(diào)用方法 mutableCopy 和 copy 來(lái)拷貝數(shù)組 array_0
NSMutableArray *array_1 = [array_0 mutableCopy];
NSArray *array_2 = [array_0 copy];
//4. 修改可變字符串 av_0
[av_0 appendString:@" Muhammed"];
//5. 最后我們輸出 array_0,array_1,array_2 中的元素醋拧,請(qǐng)先思考結(jié)果如何是什么
NSLog(@"array_0 = %@ \n array_1 = %@\n array_2 = %@",array_0,array_1,array_2);
首先我們先按照?qǐng)Dcopy_00.JPG
中關(guān)于NSMutableArray
的總結(jié)來(lái)分析下結(jié)果,根據(jù)上圖我們無(wú)論對(duì)NSMutableArray
調(diào)用mutableCopy
淀弹,還是copy
方法丹壕,都是深拷貝。因此垦页,當(dāng)我們對(duì)數(shù)組array_0
調(diào)用拷貝方法之后雀费,同時(shí)也會(huì)對(duì)其中的兩個(gè)元素av_0
和av_1
進(jìn)行拷貝。那么當(dāng)我們改變av_0
之后痊焊,對(duì)數(shù)組array_1
和array_2
中的元素av_0
應(yīng)該是沒(méi)有影響的,array_1
和array_2
的輸出仍然是Harry
忿峻。但是實(shí)際結(jié)果真的如此嗎薄啥?讓我們看看最后的輸出結(jié)果:
array_0 = (
"Harry Muhammed",
Jamie
)
array_1 = (
"Harry Muhammed",
Jamie
)
array_2 = (
"Harry Muhammed",
Jamie
)
接著我們?cè)傧驍?shù)組array_0
和array_1
中分別添加一個(gè)元素"av_2",av_3逛尚÷⒕澹看看三個(gè)數(shù)組array_0
,array_1
绰寞,array_2
中的元素個(gè)數(shù)是如何變化的到逊,它們之間是否會(huì)彼此影響:
[array_0 addObject:av_2];
[array_1 addObject:av_3];
//請(qǐng)思考 array_0,array_1,array_2 的輸出是什么?
NSLog(@"array_0 = %@ \n array_1 = %@\n array_2 = %@",array_0,array_1,array_2);
其輸出結(jié)果如下:
array_0 = (
"Harry Muhammed",
Jamie,
Micheal
)
array_1 = (
"Harry Muhammed",
Jamie,
Susan
)
array_2 = (
"Harry Muhammed",
Jamie
)
由上面的結(jié)果我們可以看出滤钱,當(dāng)我們向可變數(shù)組array_0
和array_1
中添加元素后觉壶,對(duì)數(shù)組array_02
中的元素個(gè)數(shù)并沒(méi)有影響,并且array_0
和array_1
也沒(méi)彼此影響件缸。
//創(chuàng)建一個(gè)不可變數(shù)組 array_3
NSArray *array_3 = @[av_2,av_3];
//拷貝數(shù)組 array_3 分別賦值給 array_4铜靶,array_5
NSMutableArray *array_4 = [array_3 mutableCopy];
NSArray *array_5 = [array_3 copy];
//修改數(shù)組 array_3 中的元素 av_2
[av_2 appendString:@" Jackson"];
//請(qǐng)思考 array_3,array_4,array_5 中的元素是什么?
NSLog(@"array_3 = %@ \n array_4 = %@\n array_5 = %@",array_3,array_4,array_5);
array_3 = (
"Micheal Jackson",
"Susan Boyle"
)
array_4 = (
"Micheal Jackson",
"Susan Boyle"
)
array_5 = (
"Micheal Jackson",
"Susan Boyle"
)
//創(chuàng)建數(shù)組 array_6他炊,array_7
NSMutableArray *array_6 = [[NSMutableArray alloc] initWithArray:array_3 copyItems:NO];
NSMutableArray *array_7 = [[NSMutableArray alloc] initWithArray:array_3 copyItems:YES];
[av_3 appendString:@" Boyle"];
//請(qǐng)思考 array_6争剿,array_7中的元素是什么?
NSLog(@"array_6 = %@ \n array_7 = %@",array_6,array_7);
array_6 = (
"Micheal Jackson",
"Susan Boyle"
)
array_7 = (
"Micheal Jackson",
Susan
)
2. NSArray對(duì)象的拷貝
下面讓我們?cè)賮?lái)看看對(duì)不可變數(shù)組NSArray
對(duì)象進(jìn)行拷貝的例子:
//1.創(chuàng)建四個(gè)可變字符串
NSMutableString *av_0 = [NSMutableString stringWithString:@"Harry"];
NSMutableString *av_1 = [NSMutableString stringWithString:@"Jamie"];
NSMutableString *av_2 = [NSMutableString stringWithString:@"Micheal"];
NSMutableString *av_3 = [NSMutableString stringWithString:@"Susan"];
//2.創(chuàng)建一個(gè)不可變數(shù)組 array_3
NSArray *array_3 = @[av_2,av_3];
//3.拷貝 array_3 分別賦值給 array_4痊末,array_5
NSMutableArray *array_4 = [array_3 mutableCopy];
NSArray *array_5 = [array_3 copy];
NSArray *array_6 = array_3;
//4.修改數(shù)組 array_3 中的元素 av_2
[av_2 appendString:@" Jackson"];
//5.輸出數(shù)組 array_3, array_4, array_5, array_6 中的元素蚕苇,請(qǐng)先思考結(jié)果如何
NSLog(@"array_3 = %@ \n array_4 = %@\n array_5 = %@ \n array_6 = %@",array_3,array_4,array_5,array_6);
現(xiàn)在我們也按照?qǐng)Dcopy_00.JPG
中關(guān)于NSArray
的總結(jié)來(lái)分析結(jié)果。當(dāng)我們調(diào)用copy
方法對(duì)不可變數(shù)組NSArray
的對(duì)象進(jìn)行拷貝時(shí)凿叠,是淺拷貝涩笤;而調(diào)用mutableCopy
是深拷貝嚼吞。因此,當(dāng)我們通過(guò)調(diào)用copy
方法對(duì)array_3
進(jìn)行拷貝辆它,并賦值給array_5
誊薄,此時(shí)array_3
和array_5
都指向堆中的同一塊內(nèi)存空間;而通過(guò)調(diào)用mutableCopy
方法拷貝的結(jié)果array_4
和array_3
指向堆中不同的內(nèi)存空間锰茉。那么當(dāng)我們改變av_2
時(shí)呢蔫,array_3
和array_5
中的av_2
會(huì)變化,變成Micheal Jackson
飒筑;而array_4
中的av_2
會(huì)不變化片吊,仍然是Micheal
。但實(shí)際結(jié)果真會(huì)如此嗎协屡,讓我們一起來(lái)見(jiàn)證奇跡:
array_3 = (
"Micheal Jackson",
Susan
)
array_4 = (
"Micheal Jackson",
Susan
)
array_5 = (
"Micheal Jackson",
Susan
)
array_6 = (
"Micheal Jackson",
Susan
)
我們重新創(chuàng)建一個(gè)NSArray
對(duì)象俏脊,并賦值給array_3
,并看看array_3
肤晓,array_4
爷贫,array_5
,array_6
的輸出結(jié)果补憾,代碼及運(yùn)行結(jié)果如下:
array_3 = [NSArray arrayWithObjects:av_0,av_1, nil];
NSLog(@"array_3 = %@ \n array_4 = %@\n array_5 = %@ \n array_6 = %@",array_3,array_4,array_5,array_6);
array_3 = (
Harry,
Jamie
)
array_4 = (
"Micheal Jackson",
Susan
)
array_5 = (
"Micheal Jackson",
Susan
)
array_6 = (
"Micheal Jackson",
Susan
)
4.關(guān)于 copy 和 mutableCopy
通過(guò)上面的兩個(gè)例子我們可看出漫萄,對(duì)NSArray
和NSMutableArray
對(duì)象無(wú)論通過(guò)copy
方法,還是mutableCopy
方法進(jìn)行拷貝盈匾,都是淺拷貝腾务。我們只拷貝了容器對(duì)象本身,并不復(fù)制其中的數(shù)據(jù)削饵。而copy
和mutableCopy
拷貝的結(jié)果 是可變對(duì)象和不可變對(duì)象岩瘦。無(wú)論當(dāng)前實(shí)例是否可變,若需要獲取其可變版本的拷貝窿撬,均應(yīng)該調(diào)用mutableCopy
方法启昧。同理,若需要不可變的拷貝尤仍,則均應(yīng)該調(diào)用copy
方法獲取箫津。
在Foundation框架中的所有collection類在默認(rèn)情況下都執(zhí)行淺拷貝,也就是說(shuō)只拷貝容器對(duì)象本身宰啦,而不復(fù)制其中的數(shù)據(jù)苏遥。這樣做的原因在于,容器內(nèi)的對(duì)像未必都能拷貝(即遵守copy協(xié)議)赡模,而調(diào)用者也未必想在拷貝容器時(shí)一并拷貝其中的對(duì)象田炭。
5. NSArray 和 NSMutableArray 的深拷貝
通過(guò)上面的分析我們知道,對(duì)數(shù)組對(duì)象通過(guò)copy
和mutableCopy
方法進(jìn)行拷貝漓柑,都是淺拷貝教硫。若我們想對(duì)數(shù)組進(jìn)行深拷貝叨吮,可以調(diào)用該類的初始化方法- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;
來(lái)執(zhí)行深拷貝。調(diào)用該方法的前提是瞬矩,數(shù)組中的每個(gè)元素必須遵守copy
協(xié)議茶鉴。
NSArray *array_7 = @[av_0,av_1];
NSArray *array_8 = [[NSArray alloc] initWithArray:array_7 copyItems:NO];
NSArray *array_9 = [[NSArray alloc] initWithArray:array_7 copyItems:YES];
[av_0 appendString:@" Potter"];
NSLog(@"array_7 = %@ \n array_8 = %@ \n array_9 = %@ ",array_7,array_8,array_9);
若flag
參數(shù)設(shè)為YES,則會(huì)向數(shù)組中的每個(gè)元素發(fā)送copy消息景用,用拷貝好的元素創(chuàng)建新的數(shù)組涵叮,并將其返回給調(diào)用者。當(dāng)改變av_0
時(shí)伞插,array_9
中的av_0
仍然是Harry
割粮,而array_7
和array_8
中的av_0
則變成 Harry Potter
。輸出結(jié)果如下:
array_7 = (
"Harry Potter",
Jamie
)
array_8 = (
"Harry Potter",
Jamie
)
array_9 = (
Harry,
Jamie
)
對(duì)于Foundation框架中的所有collection(字典媚污,數(shù)組舀瓢,集合)類都提供了深拷貝的初始化方法。但是對(duì)于自定義類耗美,因?yàn)闆](méi)有專門的深拷貝協(xié)議京髓,所以其具體的執(zhí)行方式由每個(gè)類來(lái)確定,我們只需決定自己所寫(xiě)的類是否要提供深拷貝的方法即可商架。此外朵锣,遵守了copy
協(xié)議的對(duì)象不一定都會(huì)執(zhí)行深拷貝。在大部分情況下甸私,執(zhí)行的都是淺拷貝。如果需要在某對(duì)象上執(zhí)行深拷貝飞傀,除非該文檔說(shuō)明它是用深拷貝來(lái)實(shí)現(xiàn)copy
協(xié)議的皇型,否則要么尋找能夠執(zhí)行深拷貝的相關(guān)方法,要么自己編寫(xiě)方法實(shí)現(xiàn)深拷貝砸烦。
6.深拷貝和淺拷貝
用一個(gè)與實(shí)例對(duì)象相同的內(nèi)容弃鸦,生成一個(gè)新的對(duì)象,這個(gè)過(guò)程就是復(fù)制幢痘。其中只復(fù)制對(duì)象的指針成為淺拷貝
唬格,而復(fù)制具有新的內(nèi)存空間的對(duì)象成為深拷貝
。
由上圖可以看出颜说,變量A指向了一個(gè)對(duì)象购岗,而這個(gè)對(duì)象的實(shí)例變量又指向了另一個(gè)對(duì)象。把變量A復(fù)制到變量B時(shí)门粪,
圖A
只復(fù)制對(duì)象的指針喊积,將對(duì)象代入到變量中;圖B
將變量A指向的對(duì)象原樣復(fù)制一份玄妈,并通過(guò)指針來(lái)共享這個(gè)對(duì)象和復(fù)制體中的實(shí)例變量乾吻;圖C
復(fù)制對(duì)象中的實(shí)例變量髓梅,并遞歸的進(jìn)行對(duì)象復(fù)制。下圖是蘋(píng)果官方文檔關(guān)于深拷貝和淺拷貝的說(shuō)明示例圖: