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

深拷貝和淺拷貝這個問題在面試中常常被問到席函,而在實際開發(fā)中,只要稍有不慎冈涧,就會在這里出現(xiàn)問題茂附。尤其對于初學者來說,我們有必要來好好研究下這個概念督弓。我會以實際代碼來演示营曼,相關(guān)示例代碼上傳至這里

首先通過一句話來解釋:深拷貝就是內(nèi)容拷貝愚隧,淺拷貝就是指針拷貝蒂阱。

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

(1)非容器類對象的深拷貝泪漂、淺拷貝

[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格式化打印出字符串對象的內(nèi)存地址。我這里主要通過內(nèi)存地址來判斷是深拷貝還是淺拷貝敌卓,在該案例中慎式,無論是深拷貝還是淺拷貝,字符串對象的值都是一樣的趟径,所以暫且就不打印值了瘪吏,而只打印地址。這里的原字符串是直接賦值的舵抹,可以發(fā)現(xiàn)肪虎,右側(cè)如果使用copy劣砍,那么打印出的內(nèi)存地址是一樣的惧蛹,表示是淺拷貝,只拷貝出了指向原對象的指針,沒有創(chuàng)建出新的對象香嗓。如果右側(cè)使用mutableCopy迅腔,那么打印出的不一樣的內(nèi)存地址,表示創(chuàng)建了一個新的對象靠娱,是深拷貝沧烈。

簡單說明一下什么是非容器類對象,像NSString像云,NSNumber這些不能包含其他對象的叫做非容器類锌雀。如NSArray和NSDictionary可以容納其他對象的叫做容器類對象。

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

-- 淺拷貝類似retain迅诬,引用計數(shù)對象+1.創(chuàng)建一個指針腋逆;

-- 深拷貝是真正意義上的拷貝,是創(chuàng)建一個新對象侈贷。copy屬性表示兩個對象內(nèi)容相同惩歉,新的對象retain為1,與原對象的引用計數(shù)無關(guān)俏蛮,原對象沒有改變撑蚌。copy減少對象對上下文的依賴。

-- retain屬性表示兩個對象地址相同(建立一個指針搏屑,指針拷貝)争涌,內(nèi)容當然相同,這個對象的retain值+1.也就是說睬棚,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)暑中。因為都是不可變字符串壹瘟,創(chuàng)建方式并不影響拷貝方式。所以可以得出結(jié)論鳄逾,對于字符串這類非容器類對象稻轨,copy是淺拷貝,mutable是深拷貝雕凹。

(3)以上測試的都是不可變字符串殴俱,這里來嘗試下可變字符串

[objc]view plaincopyprint?

//?如果是一個MutableString政冻,那么無論是copy,mutableCopy线欲,都會創(chuàng)建一個新對象明场。

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é)果如下:

可以看到李丰,對于MutableString苦锨,右側(cè)無論是copy還是mutableCopy,都會創(chuàng)建一個新對象趴泌,都是屬于深拷貝舟舒。

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

[objc]view plaincopyprint?

//?如果是一個MutableString,那么無論是copy嗜憔,mutableCopy魏蔗,都會創(chuàng)建一個新對象。

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);

我增加了四行代碼痹筛,我想要檢驗一下對一個可變字符串執(zhí)行copy和mutableCopy后莺治,返回的字符串是否可變。同時為了檢驗在哪一行出現(xiàn)crash帚稠,我提前打一個異常斷點谣旁。對于NSMutableString有appendString方法,而NSString沒有appendString方法滋早,所以這種檢驗應(yīng)該是沒有問題的榄审。運行代碼后,會在[string4 apendString:@“MMMMM”];這一行出現(xiàn)崩潰杆麸。經(jīng)過實際測試搁进,可得出以下結(jié)論:

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

-- 可變對象copy后返回的對象是不可變的饼问,mutableCopy后返回的對象是可變的。

(4)我們上面提到的都是系統(tǒng)類揭斧,如果是我們自定義的類莱革,復(fù)制結(jié)果又是怎樣呢?我下面定義一個Person類讹开,實現(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

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

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

{

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

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

person.age=self.age;

returnperson;

}

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

{

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

person.name=self.name;

person.age=self.age;

returnperson;

}

@end

首先聲明一下為什么要實現(xiàn)上述的copyWithZone和mutableCopyWithZone方法盅视?其實在iOS中并不是所有的對象都支持copy、mutableCopy方法旦万,只有遵守NSCopying協(xié)議的類可以發(fā)送copy消息闹击,遵守NSMutableCopying協(xié)議的類才可以發(fā)送mutableCopy消息,否則就會崩潰成艘。我們可以非常方便的使用系統(tǒng)類中的copy,mutableCopy方法赏半,是因為這些系統(tǒng)類本身就實現(xiàn)了NSCopying,NSMutableCopy協(xié)議梅忌,大家可以進入接口文檔進行查看捻脖。

然后編寫測試代碼如下:

[objc]view plaincopyprint?

//?Person類必須實現(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é)果如下:

破婆。

可以看到無論是用copy還是mutableCopy廷没,復(fù)制后都創(chuàng)建了一個新的對象。為什么呢确沸?因為我們本來就在copyWithZone: ,mutableCopyWithZone方法中創(chuàng)建了一個新對象啊,所以一點都不奇怪光坝。

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

[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“. 說明在執(zhí)行person.name = otherName;的時候是重新創(chuàng)建了一個字符串鸳惯。但是請大家注意,這里我name的屬性修飾符是copy叠萍,如果把copy改成strong會變成怎么樣呢芝发?

對,沒錯苛谷,name改為strong后答案就是”Jack and Mary“辅鲸。所以在這里我們可以得出以下小小的結(jié)論:

-- 因為這里的otherName是可變的,person.name的屬性是copy腹殿,所以創(chuàng)建了新的字符串独悴,屬于深拷貝;

-- 如果person.name設(shè)置為strong锣尉,那么就是淺拷貝刻炒。因為strong會持有原來的對象,使原來的對象的引用計數(shù)+1自沧,其實就是指針拷貝落蝙。

-- 當然,這里用strong還是copy暂幼,取決于實際需求筏勒。

(6)與上面(5)類似,我現(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);

其實這里很好理解旺嬉,無論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);

同樣的捐顷,無論person.name是strong還是copy荡陷,都是指針拷貝。指向的都是otherName的內(nèi)存地址迅涮。因為對不可變對象執(zhí)行copy一般都是淺拷貝废赞,這沒問題。

再次修改代碼如下:

[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);

無論person.name是strong還是copy叮姑,都是內(nèi)容拷貝唉地。創(chuàng)建一個全新的對象。因為對不可變對象執(zhí)行mutableCopy一般都是深拷貝传透,也沒問題耘沼。

(7)講了這么多非容器對象,最后我們來講講容器對象Array朱盐。先講不可變的容器對象

[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指向的是同一個對象,包括里面的元素也是指向相同的指針兵琳。

-- mutableCopyArray01和array01指向的是不同的對象狂秘,但是里面的元素指向相同的對象,mutableCopyArray01可以修改自己的對象躯肌。

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

----------------------------------------------------------------------------------------------------------------------------------------------------------------------打個分割線

上面的是不可變?nèi)萜鱊SArray羡榴,這里測試下可變?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é)論如下:

-- 容器對象和非容器對象類似,可變對象復(fù)制(copy校仑,mutableCopy)的都是一個新對象;不可變對象copy是淺復(fù)制忠售,mutableCopy是深復(fù)制。

-- 對于容器而言迄沫,元素對象始終是指針復(fù)制稻扬。

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市睬捶,隨后出現(xiàn)的幾起案子黔宛,更是在濱河造成了極大的恐慌,老刑警劉巖擒贸,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件臀晃,死亡現(xiàn)場離奇詭異觉渴,居然都是意外死亡,警方通過查閱死者的電腦和手機徽惋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門案淋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人险绘,你說我怎么就攤上這事踢京。” “怎么了隆圆?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長翔烁。 經(jīng)常有香客問我渺氧,道長,這世上最難降的妖魔是什么蹬屹? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任侣背,我火速辦了婚禮,結(jié)果婚禮上慨默,老公的妹妹穿的比我還像新娘贩耐。我一直安慰自己,他們只是感情好厦取,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布潮太。 她就那樣靜靜地躺著,像睡著了一般虾攻。 火紅的嫁衣襯著肌膚如雪铡买。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天霎箍,我揣著相機與錄音奇钞,去河邊找鬼。 笑死漂坏,一個胖子當著我的面吹牛景埃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播顶别,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼谷徙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了驯绎?” 一聲冷哼從身側(cè)響起蒂胞,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎条篷,沒想到半個月后骗随,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛤织,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年鸿染,在試婚紗的時候發(fā)現(xiàn)自己被綠了指蚜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡涨椒,死狀恐怖摊鸡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蚕冬,我是刑警寧澤免猾,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站囤热,受9級特大地震影響猎提,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜旁蔼,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一锨苏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧棺聊,春花似錦伞租、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至祟同,卻和暖如春驯击,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背耐亏。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工徊都, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人广辰。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓暇矫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親择吊。 傳聞我的和親對象是個殘疾皇子李根,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

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