iOS開(kāi)發(fā)——深拷貝與淺拷貝詳解

深拷貝和淺拷貝這個(gè)問(wèn)題在面試中常常被問(wèn)到,而在實(shí)際開(kāi)發(fā)中,只要稍有不慎,就會(huì)在這里出現(xiàn)問(wèn)題。尤其對(duì)于初學(xué)者來(lái)說(shuō),我們有必要來(lái)好好研究下這個(gè)概念。我會(huì)以實(shí)際代碼來(lái)演示,相關(guān)示例代碼上傳至這里

首先通過(guò)一句話來(lái)解釋?zhuān)荷羁截惥褪莾?nèi)容拷貝,淺拷貝就是指針拷貝。

深拷貝就是拷貝出和原來(lái)僅僅是值一樣,但是內(nèi)存地址完全不一樣的新的對(duì)象,創(chuàng)建后和原對(duì)象沒(méi)有任何關(guān)系。淺拷貝就是拷貝指向原來(lái)對(duì)象的指針,使原對(duì)象的引用計(jì)數(shù)+1,可以理解為創(chuàng)建了一個(gè)指向原對(duì)象的新指針而已,并沒(méi)有創(chuàng)建一個(gè)全新的對(duì)象。

(1)非容器類(lèi)對(duì)象的深拷貝、淺拷貝

[objc]view plaincopyprint?

//?字符串是直接賦值的,右側(cè)如果是copy,那么就是淺拷貝;右側(cè)如果是mutableCopy,那么就是深拷貝。

NSString*string1=@"helloworld";

NSString*string2=?[string1copy];//?淺拷貝

NSString*string3=?[string1mutableCopy];//?深拷貝

NSMutableString*string4=?[string1copy];//?淺拷貝

NSMutableString*string5=?[string1mutableCopy];//?深拷貝

NSLog(@"string1?=?%d;string2?=?%d",string1,string2);

NSLog(@"string1?=?%d;string3?=?%d",string1,string3);

NSLog(@"string1?=?%d;string4?=?%d",string1,string4);

NSLog(@"string1?=?%d;string5?=?%d",string1,string5);

打印結(jié)果如下:

。

我這里用%d格式化打印出字符串對(duì)象的內(nèi)存地址。我這里主要通過(guò)內(nèi)存地址來(lái)判斷是深拷貝還是淺拷貝,在該案例中,無(wú)論是深拷貝還是淺拷貝,字符串對(duì)象的值都是一樣的,所以暫且就不打印值了,而只打印地址。這里的原字符串是直接賦值的,可以發(fā)現(xiàn),右側(cè)如果使用copy,那么打印出的內(nèi)存地址是一樣的,表示是淺拷貝,只拷貝出了指向原對(duì)象的指針,沒(méi)有創(chuàng)建出新的對(duì)象。如果右側(cè)使用mutableCopy,那么打印出的不一樣的內(nèi)存地址,表示創(chuàng)建了一個(gè)新的對(duì)象,是深拷貝。

簡(jiǎn)單說(shuō)明一下什么是非容器類(lèi)對(duì)象,像NSString,NSNumber這些不能包含其他對(duì)象的叫做非容器類(lèi)。如NSArray和NSDictionary可以容納其他對(duì)象的叫做容器類(lèi)對(duì)象。

做一個(gè)小小的總結(jié):

-- 淺拷貝類(lèi)似retain,引用計(jì)數(shù)對(duì)象+1.創(chuàng)建一個(gè)指針;

-- 深拷貝是真正意義上的拷貝,是創(chuàng)建一個(gè)新對(duì)象。copy屬性表示兩個(gè)對(duì)象內(nèi)容相同,新的對(duì)象retain為1,與原對(duì)象的引用計(jì)數(shù)無(wú)關(guān),原對(duì)象沒(méi)有改變。copy減少對(duì)象對(duì)上下文的依賴(lài)。

-- retain屬性表示兩個(gè)對(duì)象地址相同(建立一個(gè)指針,指針拷貝),內(nèi)容當(dāng)然相同,這個(gè)對(duì)象的retain值+1.也就是說(shuō),retain是指針拷貝,copy是內(nèi)容拷貝。

(2)改變字符串的創(chuàng)建方式,使用stringWithFormat創(chuàng)建字符串,而不是直接賦值

[objc]view plaincopyprint?

//?結(jié)果同上。右側(cè)如果是copy,那么就是淺拷貝;右側(cè)如果是mutableCopy,那么就是深拷貝。

NSString*string1=?[NSStringstringWithFormat:@"helloworld"];

NSString*string2=?[string1copy];//?淺拷貝

NSString*string3=?[string1mutableCopy];//?深拷貝

NSMutableString*string4=?[string1copy];//?淺拷貝

NSMutableString*string5=?[string1mutableCopy];//?深拷貝

NSLog(@"string1?=?%d;string2?=?%d",string1,string2);

NSLog(@"string1?=?%d;string3?=?%d",string1,string3);

NSLog(@"string1?=?%d;string4?=?%d",string1,string4);

NSLog(@"string1?=?%d;string5?=?%d",string1,string5);

打印結(jié)果如下:

。

結(jié)果同(1)。因?yàn)槎际遣豢勺冏址?,?chuàng)建方式并不影響拷貝方式。所以可以得出結(jié)論,對(duì)于字符串這類(lèi)非容器類(lèi)對(duì)象,copy是淺拷貝,mutable是深拷貝。

(3)以上測(cè)試的都是不可變字符串,這里來(lái)嘗試下可變字符串

[objc]view plaincopyprint?

//?如果是一個(gè)MutableString,那么無(wú)論是copy,mutableCopy,都會(huì)創(chuàng)建一個(gè)新對(duì)象。

NSMutableString*string1=?[NSMutableStringstringWithString:@"helloworld"];

NSString*string2=?[string1copy];//?深拷貝

NSString*string3=?[string1mutableCopy];//?深拷貝

NSMutableString*string4=?[string1copy];//?深拷貝

NSMutableString*string5=?[string1mutableCopy];//?深拷貝

NSLog(@"string1?=?%d;string2?=?%d",string1,string2);

NSLog(@"string1?=?%d;string3?=?%d",string1,string3);

NSLog(@"string1?=?%d;string4?=?%d",string1,string4);

NSLog(@"string1?=?%d;string5?=?%d",string1,string5);

打印結(jié)果如下:

可以看到,對(duì)于MutableString,右側(cè)無(wú)論是copy還是mutableCopy,都會(huì)創(chuàng)建一個(gè)新對(duì)象,都是屬于深拷貝。

現(xiàn)在我們對(duì)以上代碼做一點(diǎn)修改:

[objc]view plaincopyprint?

//?如果是一個(gè)MutableString,那么無(wú)論是copy,mutableCopy,都會(huì)創(chuàng)建一個(gè)新對(duì)象。

NSMutableString*string1=?[NSMutableStringstringWithString:@"helloworld"];

NSString*string2=?[string1copy];//?深拷貝

NSString*string3=?[string1mutableCopy];//?深拷貝

NSMutableString*string4=?[string1copy];//?深拷貝

NSMutableString*string5=?[string1mutableCopy];//?深拷貝

NSLog(@"string1?=?%d;string2?=?%d",string1,string2);

NSLog(@"string1?=?%d;string3?=?%d",string1,string3);

NSLog(@"string1?=?%d;string4?=?%d",string1,string4);

NSLog(@"string1?=?%d;string5?=?%d",string1,string5);

//?增加以下代碼

[string4appendString:@"MMMMMM"];

[string5appendString:@"11111"];

NSLog(@"string4?=?%@",string4);

NSLog(@"string5?=?%@",string5);

我增加了四行代碼,我想要檢驗(yàn)一下對(duì)一個(gè)可變字符串執(zhí)行copy和mutableCopy后,返回的字符串是否可變。同時(shí)為了檢驗(yàn)在哪一行出現(xiàn)crash,我提前打一個(gè)異常斷點(diǎn)。對(duì)于NSMutableString有appendString方法,而NSString沒(méi)有appendString方法,所以這種檢驗(yàn)應(yīng)該是沒(méi)有問(wèn)題的。運(yùn)行代碼后,會(huì)在[string4 apendString:@“MMMMM”];這一行出現(xiàn)崩潰。經(jīng)過(guò)實(shí)際測(cè)試,可得出以下結(jié)論:

-- 對(duì)于可變對(duì)象的復(fù)制,都是深拷貝;

-- 可變對(duì)象copy后返回的對(duì)象是不可變的,mutableCopy后返回的對(duì)象是可變的。

(4)我們上面提到的都是系統(tǒng)類(lèi),如果是我們自定義的類(lèi),復(fù)制結(jié)果又是怎樣呢?我下面定義一個(gè)Person類(lèi),實(shí)現(xiàn)如下:

Person.h

[objc]view plaincopyprint?

#import?

@interfacePerson?:?NSObject

@property(nonatomic,copy)NSString*name;

@property(nonatomic,?assign)?NSInteger?age;

@end

Person.m

[objc]view plaincopyprint?

#import?"Person.h"

@interfacePerson()

@end

@implementationPerson

//?對(duì)應(yīng)copy方法

-?(id)copyWithZone:(NSZone*)zone

{

Person*person?=?[[PersonallocWithZone:zone]init];

person.name=self.name;//?這里的self就是被copy的對(duì)象

person.age=self.age;

returnperson;

}

-?(id)mutableCopyWithZone:(NSZone*)zone

{

Person*person?=?[[PersonallocWithZone:zone]init];

person.name=self.name;

person.age=self.age;

returnperson;

}

@end

首先聲明一下為什么要實(shí)現(xiàn)上述的copyWithZone和mutableCopyWithZone方法?其實(shí)在iOS中并不是所有的對(duì)象都支持copy、mutableCopy方法,只有遵守NSCopying協(xié)議的類(lèi)可以發(fā)送copy消息,遵守NSMutableCopying協(xié)議的類(lèi)才可以發(fā)送mutableCopy消息,否則就會(huì)崩潰。我們可以非常方便的使用系統(tǒng)類(lèi)中的copy,mutableCopy方法,是因?yàn)檫@些系統(tǒng)類(lèi)本身就實(shí)現(xiàn)了NSCopying,NSMutableCopy協(xié)議,大家可以進(jìn)入接口文檔進(jìn)行查看。

然后編寫(xiě)測(cè)試代碼如下:

[objc]view plaincopyprint?

//?Person類(lèi)必須實(shí)現(xiàn)copyWithZone和mutableCopyWithZone方法。

Person*person?=?[[Personalloc]init];

person.name=@"Jack";

person.age=23;

Person*copyPerson?=?[personcopy];//?深拷貝

Person*mutableCopyPerson?=?[personmutableCopy];//?深拷貝

NSLog(@"person?=?%d;copyPerson?=?%d",person,copyPerson);

NSLog(@"person?=?%d;mutableCopyPerson?=?%d",person,mutableCopyPerson);

打印結(jié)果如下:

。

可以看到無(wú)論是用copy還是mutableCopy,復(fù)制后都創(chuàng)建了一個(gè)新的對(duì)象。為什么呢?因?yàn)槲覀儽緛?lái)就在copyWithZone: ,mutableCopyWithZone方法中創(chuàng)建了一個(gè)新對(duì)象啊,所以一點(diǎn)都不奇怪。

(5)從上述(4)中可以看到我的Person自定義對(duì)象在copy后是創(chuàng)建了一個(gè)全新的對(duì)象,那么這個(gè)對(duì)象中的屬性呢?這些屬性是深拷貝還是淺拷貝呢?

[objc]view plaincopyprint?

NSMutableString*otherName?=?[[NSMutableStringalloc]initWithString:@"Jack"];

Person*person?=?[[Personalloc]init];

person.name=?otherName;

person.age=23;

[otherNameappendString:@"?and?Mary"];

NSLog(@"person.name?=?%@",person.name);

打印結(jié)果如下:

。

打印的結(jié)果是"Jack",而不是”Jack and Mary“. 說(shuō)明在執(zhí)行person.name = otherName;的時(shí)候是重新創(chuàng)建了一個(gè)字符串。但是請(qǐng)大家注意,這里我name的屬性修飾符是copy,如果把copy改成strong會(huì)變成怎么樣呢?

對(duì),沒(méi)錯(cuò),name改為strong后答案就是”Jack and Mary“。所以在這里我們可以得出以下小小的結(jié)論:

-- 因?yàn)檫@里的otherName是可變的,person.name的屬性是copy,所以創(chuàng)建了新的字符串,屬于深拷貝;

-- 如果person.name設(shè)置為strong,那么就是淺拷貝。因?yàn)閟trong會(huì)持有原來(lái)的對(duì)象,使原來(lái)的對(duì)象的引用計(jì)數(shù)+1,其實(shí)就是指針拷貝。

-- 當(dāng)然,這里用strong還是copy,取決于實(shí)際需求。

(6)與上面(5)類(lèi)似,我現(xiàn)在修改代碼如下:

[objc]view plaincopyprint?

NSString*otherName?=@"jack";

Person*person?=?[[Personalloc]init];

person.name=?otherName;

person.age=23;

NSLog(@"othername?=?%d;person.name?=?%d",otherName,person.name);

其實(shí)這里很好理解,無(wú)論person.name是strong還是copy,都是指針拷貝。指向的都是otherName的內(nèi)存地址。

現(xiàn)修改代碼如下:

[objc]view plaincopyprint?

NSString*otherName?=@"jack";

Person*person?=?[[Personalloc]init];

person.name=?[otherNamecopy];

person.age=23;

NSLog(@"othername?=?%d;person.name?=?%d",otherName,person.name);

同樣的,無(wú)論person.name是strong還是copy,都是指針拷貝。指向的都是otherName的內(nèi)存地址。因?yàn)閷?duì)不可變對(duì)象執(zhí)行copy一般都是淺拷貝,這沒(méi)問(wèn)題。

再次修改代碼如下:

[objc]view plaincopyprint?

NSString*otherName?=@"jack";

Person*person?=?[[Personalloc]init];

person.name=?[otherNamemutableCopy];

person.age=23;

NSLog(@"othername?=?%d;person.name?=?%d",otherName,person.name);

無(wú)論person.name是strong還是copy,都是內(nèi)容拷貝。創(chuàng)建一個(gè)全新的對(duì)象。因?yàn)閷?duì)不可變對(duì)象執(zhí)行mutableCopy一般都是深拷貝,也沒(méi)問(wèn)題。

(7)講了這么多非容器對(duì)象,最后我們來(lái)講講容器對(duì)象Array。先講不可變的容器對(duì)象

[objc]view plaincopyprint?

NSArray*array01=?[NSArrayarrayWithObjects:@"a",@"b",@"c",nilnil];

NSArray*copyArray01=?[array01copy];

NSMutableArray*mutableCopyArray01=?[array01mutableCopy];

NSLog(@"array01?=?%d,copyArray01?=?%d",array01,copyArray01);

NSLog(@"array01?=?%d,mutableCopyArray01?=?%d",array01,mutableCopyArray01);

NSLog(@"array01[0]?=?%d,array01[1]?=?%d,array01[2]?=?%d",array01[0],array01[1],array01[2]);

NSLog(@"copyArray01[0]?=?%d,copyArray01[1]?=?%d,copyArray01[2]?=?%d",copyArray01[0],copyArray01[1],copyArray01[2]);

NSLog(@"mutableCopyArray01[0]?=?%d,mutableCopyArray01[1]?=?%d,mutableCopyArray01[2]?=?%d",mutableCopyArray01[0],mutableCopyArray01[1],mutableCopyArray01[2]);

打印結(jié)果如下:

。

我們可以得出以下結(jié)論:

-- copyArray01和array01指向的是同一個(gè)對(duì)象,包括里面的元素也是指向相同的指針。

-- mutableCopyArray01和array01指向的是不同的對(duì)象,但是里面的元素指向相同的對(duì)象,mutableCopyArray01可以修改自己的對(duì)象。

--?copyArray01是對(duì)array01的指針復(fù)制(淺復(fù)制),而mutableCopyArray01是內(nèi)容復(fù)制。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------打個(gè)分割線

上面的是不可變?nèi)萜鱊SArray,這里測(cè)試下可變?nèi)萜鱊SMutableArray:

[objc]view plaincopyprint?

NSMutableArray*array01=?[NSMutableArrayarrayWithObjects:@"a",@"b",@"c",nilnil];

NSArray*copyArray01=?[array01copy];

NSMutableArray*mutableCopyArray01=?[array01mutableCopy];

NSLog(@"array01?=?%d,copyArray01?=?%d",array01,copyArray01);

NSLog(@"array01?=?%d,mutableCopyArray01?=?%d",array01,mutableCopyArray01);

NSLog(@"array01[0]?=?%d,array01[1]?=?%d,array01[2]?=?%d",array01[0],array01[1],array01[2]);

NSLog(@"copyArray01[0]?=?%d,copyArray01[1]?=?%d,copyArray01[2]?=?%d",copyArray01[0],copyArray01[1],copyArray01[2]);

NSLog(@"mutableCopyArray01[0]?=?%d,mutableCopyArray01[1]?=?%d,mutableCopyArray01[2]?=?%d",mutableCopyArray01[0],mutableCopyArray01[1],mutableCopyArray01[2]);

打印的結(jié)果如下:

。

可得結(jié)論如下:

-- 容器對(duì)象和非容器對(duì)象類(lèi)似,可變對(duì)象復(fù)制(copy,mutableCopy)的都是一個(gè)新對(duì)象;不可變對(duì)象copy是淺復(fù)制,mutableCopy是深復(fù)制。

-- 對(duì)于容器而言,元素對(duì)象始終是指針復(fù)制。

以上涉及的也只是一部分,想要對(duì)深拷貝、淺拷貝有更為深入的了解,必須首先要對(duì)內(nèi)存管理較為熟悉,然后在實(shí)際開(kāi)發(fā)中不斷去實(shí)踐,才會(huì)隨心所欲。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容