原文連接http://www.reibang.com/p/ebbac2fec4c6
一踪少、從面向?qū)ο蟮絆bjective-C概覽copy
1、面向?qū)ο螅?/p>
In object-oriented programming, object copying is creating a copy of an existing object, a unit of data in object-oriented programming. The resulting object is called an object copy or simply copy of the original object. Copying is basic but has subtleties and can have significant overhead. There are several ways to copy an object, most commonly by a copy constructor or cloning. Copying is done mostly so the copy can be modified or moved, or the current value preserved. If either of these is unneeded, a reference to the original data is sufficient and more efficient, as no copying occurs.
在面向?qū)ο蟮某绦蛟O(shè)計(jì)中糠涛,對(duì)象的copy就是創(chuàng)建一個(gè)已經(jīng)存在的對(duì)象的copy援奢。這種對(duì)象的創(chuàng)建的結(jié)果被稱為原始對(duì)象的copy。copy是很基礎(chǔ)的忍捡,但是也有其精巧的地方集漾,并且可能造成巨大的消耗。有很多種方式可以copy對(duì)象砸脊,最常用的就是copy構(gòu)造器和克隆具篇。copy經(jīng)常用于對(duì)象的修改、移動(dòng)和保護(hù)凌埂。如果上述的幾種應(yīng)用都不需要驱显,持有原始對(duì)象的引用就足夠了,并不需要copy侨舆。
2秒紧、OC:
In Objective-C, the methods copy and mutableCopy are inherited by all objects and intended for performing copies; the latter is for creating a mutable type of the original object. These methods in turn call the copyWithZone and mutableCopyWithZone methods, respectively, to perform the copying. An object must implement the corresponding copyWithZone method to be copyable.
在OC中绢陌,copy和mutableCopy兩個(gè)方法是被所有對(duì)象繼承的(有點(diǎn)小毛病挨下,應(yīng)該指所有繼承自NSObject的類),這兩個(gè)方法就是為copy準(zhǔn)備的脐湾。其中臭笆,mutableCopy是為了創(chuàng)建原始對(duì)象的可變類型的copy。這兩個(gè)方法分別調(diào)用copyWithZone和mutableCopyWithZone兩個(gè)方法來(lái)進(jìn)行copy秤掌。一個(gè)類必須實(shí)現(xiàn)copyWithZone或者mutableCopyWithZone愁铺,才能進(jìn)行copy或者mutableCopy。
那么闻鉴,我們可以從以上獲取到什么信息?
copy經(jīng)常用于對(duì)象的修改茵乱、移動(dòng)和保護(hù)。如果上述的幾種應(yīng)用都不需要孟岛,持有原始對(duì)象的引用就足夠了瓶竭,并不需要copy。
一個(gè)類必須實(shí)現(xiàn)copyWithZone或者mutableCopyWithZone渠羞,才能進(jìn)行copy或者mutableCopy斤贰。
下一階段,本文將展開(kāi)講述OC中的copy相關(guān)信息以及如何使用copy方法次询。
二荧恍、Objective-C中copy相關(guān)
1、OC中的copy相關(guān)內(nèi)容
在XCode 里Foundation.framework下的Headers里屯吊,也在系統(tǒng)里找到原文件:/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSObject.h
@protocolNSCopying-(id)copyWithZone:(nullable NSZone*)zone;@end@protocolNSMutableCopying-(id)mutableCopyWithZone:(nullable NSZone*)zone;@end
在/usr/include/objc 下面找到 runtime 的 NSObject.h
-(id)copy;-(id)mutableCopy;
修飾屬性的關(guān)鍵字copy
2送巡、這里需要注意的有以下幾點(diǎn)
若想使用copy和mutableCopy摹菠,需要分別實(shí)現(xiàn)NSCopying協(xié)議和NSMutableCopying協(xié)議,即實(shí)現(xiàn)copyWithZone:和mutableCopyWithZone:方法授艰。
繼承自NSObject的大部分框架類均默認(rèn)實(shí)現(xiàn)了NSCopying辨嗽,并且一些具備可變類型的類如NSString、NSArray淮腾、NSDictionary糟需,以及它們的可變類型類NSMutableString、NSMutableArray和NSMutableDictionary也實(shí)現(xiàn)了NSMutableCopying谷朝。(查了大部分常用類洲押,均實(shí)現(xiàn)了NSCopying,所以暫時(shí)這么說(shuō)吧圆凰,可能有人說(shuō)NSNumber并沒(méi)有實(shí)現(xiàn)NSCopying杈帐,那你可以看一下它的父類NSValue,其實(shí)現(xiàn)了NSCopying)
對(duì)于一些自定義類专钉,需要自己實(shí)現(xiàn)NSCopying挑童。具體方式且看下部分。
三跃须、非容器對(duì)象的深淺copy
首先站叼,我們談一下非容器對(duì)象的深淺copy,這些非容器對(duì)象菇民,包含常用的NSString尽楔、NSNumber等,也包括我們自定義的一些非容器類的實(shí)例第练。下面分三個(gè)三面進(jìn)行分析阔馋。
1、首先說(shuō)說(shuō)深淺copy
準(zhǔn)則
淺copy:指針復(fù)制娇掏,不會(huì)創(chuàng)建一個(gè)新的對(duì)象呕寝。
深copy:內(nèi)容復(fù)制,會(huì)創(chuàng)建一個(gè)新的對(duì)象婴梧。
此處下梢,不進(jìn)行過(guò)多的解釋,從下面的結(jié)果分析中志秃,按例子來(lái)理解怔球。
2、框架類的深淺copy
準(zhǔn)則
探究框架類深copy還是淺copy浮还,需要清楚的是該類如何實(shí)現(xiàn)的NSCopying和NSMutableCopy的兩個(gè)方法copyWithZone:和mutableCopyWithZone:竟坛。然而OC并不開(kāi)源,并且本文這里也不會(huì)進(jìn)行源碼的推測(cè)。
那么担汤,我們應(yīng)該遵循怎樣一個(gè)原則呢涎跨?如下:
對(duì)immutableObject,即不可變對(duì)象崭歧,執(zhí)行copy隅很,會(huì)得到不可變對(duì)象,并且是淺copy率碾。
對(duì)immutableObject叔营,即不可變對(duì)象,執(zhí)行mutableCopy所宰,會(huì)得到可變對(duì)象绒尊,并且是深copy。
對(duì)mutableObject仔粥,即可變對(duì)象婴谱,執(zhí)行copy,會(huì)得到不可變對(duì)象躯泰,并且是深copy谭羔。
對(duì)mutableObject,即可變對(duì)象麦向,執(zhí)行mutableCopy瘟裸,會(huì)得到可變對(duì)象,并且是深copy磕蛇。
代碼
// 此處以NSString為例探究框架類深淺copy// 不可變對(duì)象NSString*str=@"1";NSString*str1=[str copy];NSString*str2=[str mutableCopy];// 可變對(duì)象NSMutableString*mutableStr=[NSMutableString stringWithString:@"1"];NSMutableString*mutableStr1=[mutableStr copy];NSMutableString*mutableStr2=[mutableStr mutableCopy];// 打印對(duì)象的指針來(lái)確認(rèn)是否創(chuàng)建了一個(gè)新的對(duì)象// 不可變對(duì)象原始指針NSLog(@"%p",str);// 不可變對(duì)象copy后指針NSLog(@"%p",str1);// 不可變對(duì)象mutalbeCopy后指針NSLog(@"%p",str2);// 可變對(duì)象原始指針NSLog(@"%p",mutableStr);// 可變對(duì)象copy后指針NSLog(@"%p",mutableStr1);// 可變對(duì)象mutalbeCopy后指針NSLog(@"%p",mutableStr2);
結(jié)果分析
// 此處依次對(duì)應(yīng)上述6個(gè)log景描,可見(jiàn)與前面所講的原則吻合(此處不驗(yàn)證可變類型和不可變類型十办,默認(rèn)上述原則正確即可)秀撇。2016-10-21 10:50:52.879 Memory[67680:5623387] 0x10d85a1b02016-10-21 10:50:52.879 Memory[67680:5623387] 0x10d85a1b02016-10-21 10:50:52.879 Memory[67680:5623387] 0x60800007a0802016-10-21 10:50:52.879 Memory[67680:5623387] 0x60800007a9c02016-10-21 10:50:52.880 Memory[67680:5623387] 0xa0000000000003112016-10-21 10:50:52.880 Memory[67680:5623387] 0x60800007a900
3、自定義類的深淺copy
準(zhǔn)則
對(duì)于一個(gè)我們自定義的類型向族,顯然比框架類容易操縱的多呵燕。此處就拿NSCopying舉例(因?yàn)閺臎](méi)有自定義過(guò)具有可變類型的類,當(dāng)然件相,如果有需要的話再扭,也可以實(shí)現(xiàn)NSMutableCopying)碎连。自定義的類就和2中的原則沒(méi)有半毛錢關(guān)系了付材,一切就看你怎么實(shí)現(xiàn)NSCopying協(xié)議中的copyWithZone:方法。
代碼
// Model定義谍夭,copyWithZone第一種實(shí)現(xiàn)(淺copy)@interfaceModel1:NSObject<NSCopying>@property(nonatomic,assign)NSInteger a;@end@implementationModel1-(id)copyWithZone:(NSZone*)zone{returnself;}@end// Model定義紊撕,copyWithZone第二種實(shí)現(xiàn)(深copy)@interfaceModel1:NSObject<NSCopying>@property(nonatomic,assign)NSInteger a;@end@implementationModel1-(id)copyWithZone:(NSZone*)zone{Model1*model=[[Model1 allocWithZone:zone]init];model.a=self.a;returnmodel;}@end// 分別選擇上述兩種model進(jìn)行指針打印罢荡。Model1*model=[[Model1 alloc]init];Model1*copyModel=[model copy];NSLog(@"%p",model);NSLog(@"%p",copyModel);
結(jié)果分析
// 對(duì)應(yīng)上述一,可見(jiàn)實(shí)現(xiàn)了淺copy2016-10-2111:12:03.149Memory[67723:5636292]0x60000000c9d02016-10-2111:12:03.149Memory[67723:5636292]0x60000000c9d0// 對(duì)應(yīng)上述二,可見(jiàn)實(shí)現(xiàn)了深copy2016-10-2111:16:46.803Memory[67752:5640133]0x60800001df002016-10-2111:16:46.803Memory[67752:5640133]0x60800001def0
四区赵、容器對(duì)象的深淺copy
前文已經(jīng)知道了深淺copy的區(qū)別惭缰,你也大致猜到了為什么將容器對(duì)象拿出來(lái)作為一塊。對(duì)笼才,因?yàn)槿萜髦锌赡馨芏鄬?duì)象漱受,而這些對(duì)象也需要區(qū)分深淺copy。往深里說(shuō)骡送,容器中可能包含容器對(duì)象昂羡,那更是麻煩了。不要急摔踱,看下面紧憾,以NSArray的深淺copy為例,將容器的深淺copy分為四種昌渤。
1赴穗、淺copy
準(zhǔn)則
容器的淺copy,符合三.2中的原則膀息。
代碼
// 和NSString淺copy的驗(yàn)證步驟一樣NSArray*arr=[NSArray arrayWithObjects:@"1",nil];NSArray*copyArr=[arr copy];NSLog(@"%p",arr);NSLog(@"%p",copyArr);
結(jié)果分析
// 無(wú)疑是淺copy(你可能會(huì)問(wèn)般眉,為什么不看一下arr和copyArr內(nèi)部元素的指針對(duì)比?這里并沒(méi)有必要潜支,最外層對(duì)象都沒(méi)有創(chuàng)建新的甸赃,里面不用驗(yàn)證)2016-10-2111:27:57.554Memory[67778:5646253]0x6000000106902016-10-2111:27:57.554Memory[67778:5646253]0x600000010690
2、單層深copy
準(zhǔn)則
容器的單層深copy冗酿,符合三.2中的原則(只是深copy變成了單層深copy)埠对。這里的單層指的是完成了NSArray對(duì)象的深copy,而未對(duì)其容器內(nèi)對(duì)象進(jìn)行處理裁替。
代碼
NSArray*arr=[NSArray arrayWithObjects:@"1",nil];NSArray*copyArr=[arr mutableCopy];NSLog(@"%p",arr);NSLog(@"%p",copyArr);// 打印arr项玛、copyArr內(nèi)部元素進(jìn)行對(duì)比NSLog(@"%p",arr[0]);NSLog(@"%p",copyArr[0]);
結(jié)果分析
// 可發(fā)現(xiàn)前兩項(xiàng)地址不同,即完成深copy弱判,但是后兩項(xiàng)相同襟沮,這代表容器內(nèi)部的元素并沒(méi)有完成深copy,所有稱之為單層深copy2016-10-2111:32:27.157Memory[67801:5649757]0x6000000030d02016-10-2111:32:27.157Memory[67801:5649757]0x600000242e502016-10-2111:32:27.157Memory[67801:5649757]0x10dd811b02016-10-2111:32:27.157Memory[67801:5649757]0x10dd811b0
3昌腰、雙層深copy
準(zhǔn)則
容器的雙層深copy已經(jīng)脫離了三.2中的原則开伏。這里的雙層指的是完成了NSArray對(duì)象和NSArray容器內(nèi)對(duì)象的深copy(為什么不說(shuō)完全,是因?yàn)闊o(wú)法處理NSArray中還有一個(gè)NSArray這種情況)遭商。
代碼
// 隨意創(chuàng)建一個(gè)NSMutableString對(duì)象NSMutableString*mutableString=[NSMutableString stringWithString:@"1"];// 隨意創(chuàng)建一個(gè)包涵NSMutableString的NSMutableArray對(duì)象NSMutableString*mutalbeString1=[NSMutableString stringWithString:@"1"];NSMutableArray*mutableArr=[NSMutableArray arrayWithObjects:mutalbeString1,nil];// 將mutableString和mutableArr放入一個(gè)新的NSArray中NSArray*testArr=[NSArray arrayWithObjects:mutableString,mutableArr,nil];// 通過(guò)官方文檔提供的方式創(chuàng)建copyNSArray*testArrCopy=[[NSArray alloc]initWithArray:testArr copyItems:YES];// testArr和testArrCopy指針對(duì)比NSLog(@"%p",testArr);NSLog(@"%p",testArrCopy);// testArr和testArrCopy中元素指針對(duì)比// mutableString對(duì)比NSLog(@"%p",testArr[0]);NSLog(@"%p",testArrCopy[0]);// mutableArr對(duì)比NSLog(@"%p",testArr[1]);NSLog(@"%p",testArrCopy[1]);// mutableArr中的元素對(duì)比固灵,即mutalbeString1對(duì)比NSLog(@"%p",testArr[1][0]);NSLog(@"%p",testArrCopy[1][0]);
結(jié)果分析
// 這里可以發(fā)現(xiàn),copy后劫流,只有mutableArr中的mutalbeString1指針地址沒(méi)有變化巫玻。而testArr的指針和testArr中的mutableArr暑认、mutableString的指針地址均發(fā)生變化。所以稱之為雙層深復(fù)制大审。2016-10-2112:03:15.549Memory[67855:5668888]0x60800003c7a02016-10-2112:03:15.549Memory[67855:5668888]0x60800003c8802016-10-2112:03:15.549Memory[67855:5668888]0x6080002605402016-10-2112:03:15.550Memory[67855:5668888]0xa0000000000003112016-10-2112:03:15.550Memory[67855:5668888]0x60800005d6102016-10-2112:03:15.550Memory[67855:5668888]0x60800000d2e02016-10-2112:03:15.550Memory[67855:5668888]0x6080002609802016-10-2112:03:15.550Memory[67855:5668888]0x608000260980
限制
initWithArray: copyItems:會(huì)使NSArray中元素均執(zhí)行copy方法蘸际。這也是我在testArr中放入NSMutableArray和NSMutableString的原因。如果我放入的是NSArray或者NSString徒扶,執(zhí)行copy后粮彤,只會(huì)發(fā)生指針復(fù)制;如果我放入的是未實(shí)現(xiàn)NSCopying協(xié)議的對(duì)象姜骡,調(diào)用這個(gè)方法甚至?xí)rash导坟。這里,官方文檔的描述有誤圈澈。
If the objects in the collection have adopted the?NSCopying
protocol, the objects are deeply copied to the new collection, which is then the sole owner of the copied objects.
4惫周、完全深copy
準(zhǔn)則
如果想完美的解決NSArray嵌套NSArray這種情形,可以使用歸檔康栈、解檔的方式递递。
代碼
// 隨意創(chuàng)建一個(gè)NSMutableString對(duì)象NSMutableString*mutableString=[NSMutableString stringWithString:@"1"];// 隨意創(chuàng)建一個(gè)包涵NSMutableString的NSMutableArray對(duì)象NSMutableString*mutalbeString1=[NSMutableString stringWithString:@"1"];NSMutableArray*mutableArr=[NSMutableArray arrayWithObjects:mutalbeString1,nil];// 將mutableString和mutableArr放入一個(gè)新的NSArray中NSArray*testArr=[NSArray arrayWithObjects:mutableString,mutableArr,nil];// 通過(guò)歸檔、解檔方式創(chuàng)建copyNSArray*testArrCopy=[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:testArr]];;// testArr和testArrCopy指針對(duì)比NSLog(@"%p",testArr);NSLog(@"%p",testArrCopy);// testArr和testArrCopy中元素指針對(duì)比// mutableString對(duì)比NSLog(@"%p",testArr[0]);NSLog(@"%p",testArrCopy[0]);// mutableArr對(duì)比NSLog(@"%p",testArr[1]);NSLog(@"%p",testArrCopy[1]);// mutableArr中的元素對(duì)比啥么,即mutalbeString1對(duì)比NSLog(@"%p",testArr[1][0]);NSLog(@"%p",testArrCopy[1][0]);
結(jié)果分析
// 可見(jiàn)完成了完全深復(fù)制登舞,testArr和testArrCopy中的元素,以及容器中容器的指針地址完全不同悬荣,所以完成了完全深復(fù)制菠秒。2016-10-2112:19:34.022Memory[67887:5677318]0x60800002db002016-10-2112:19:34.022Memory[67887:5677318]0x60800002dc202016-10-2112:19:34.022Memory[67887:5677318]0x6080002604002016-10-2112:19:34.023Memory[67887:5677318]0x6080002603c02016-10-2112:19:34.023Memory[67887:5677318]0x608000051d902016-10-2112:19:34.023Memory[67887:5677318]0x6080000521e02016-10-2112:19:34.023Memory[67887:5677318]0x6080002606002016-10-2112:19:34.023Memory[67887:5677318]0x6080002606c0
限制
歸檔和解檔的前提是NSArray中所有的對(duì)象都實(shí)現(xiàn)了NSCoding協(xié)議。
五氯迂、拾遺
1践叠、關(guān)鍵字copy
代碼與結(jié)果
// 首先分別給出copy和strong修飾的屬性,以NSString舉例// 1嚼蚀、strong@property(nonatomic,strong)NSString*str;// 2禁灼、copy@property(nonatomic,copy)NSString*str;// 分別對(duì)1和2執(zhí)行下述代碼NSMutableString*mutableStr=[NSMutableString stringWithFormat:@"123"];self.str=mutableStr;[mutableStr appendString:@"456"];NSLog(@"%@",self.str);NSLog(@"%p",self.str);NSLog(@"%@",mutableStr);NSLog(@"%p",mutableStr);// 結(jié)果12016-10-2114:08:46.657Memory[68242:5714288]1234562016-10-2114:08:46.657Memory[68242:5714288]0x6080000710402016-10-2114:08:46.657Memory[68242:5714288]1234562016-10-2114:08:46.657Memory[68242:5714288]0x608000071040// 結(jié)果22016-10-2114:11:16.879Memory[68264:5716282]1232016-10-2114:11:16.880Memory[68264:5716282]0xa0000000033323132016-10-2114:11:16.880Memory[68264:5716282]1234562016-10-2114:11:16.880Memory[68264:5716282]0x60000007bbc0
分析
結(jié)果1為strong修飾的結(jié)果,可見(jiàn)** [mutableStr appendString:@"456"]**修改mutableStr造成了self.str的改變驰坊,顯然不安全匾二;結(jié)果2為copy修飾的結(jié)果哮独,可見(jiàn)?[mutableStr appendString:@"456"]修改mutableStr未造成self.str的改變拳芙,顯然安全。(從內(nèi)存地址的變化也可以看出來(lái))
這里可以推測(cè)出皮璧,copy關(guān)鍵字是在str屬性的set方法里面返回了mutableStr的copy舟扎,而strong關(guān)鍵字僅僅是返回了mutableStr。
2悴务、深淺copy對(duì)引用計(jì)數(shù)的影響
淺copy睹限,類似strong譬猫,持有原始對(duì)象的指針,會(huì)使retainCount加一羡疗。
深copy染服,會(huì)創(chuàng)建一個(gè)新的對(duì)象,不會(huì)對(duì)原始對(duì)象的retainCount變化叨恨。
// 也許你會(huì)疑問(wèn)arc下如何訪問(wèn)retainCount屬性柳刮,這里提供了兩種方式(下面代碼中a代表一個(gè)任意對(duì)象,這個(gè)對(duì)象最好不要是NSString和NSNumber痒钝,因?yàn)橛盟鼈冞M(jìn)行測(cè)試會(huì)出問(wèn)題)// kvc方式NSLog(@"Retain count is %ld",CFGetRetainCount((__bridge CFTypeRef)a));// 橋接字方式NSLog(@"Retain count %@",[a valueForKey:@"retainCount"]);
3秉颗、可變和不可變
可變和不可變上文談的不是很多,因?yàn)楸疚恼J(rèn)為這完全與NSCopying和NSMutableCopying的實(shí)現(xiàn)息息相關(guān)送矩。當(dāng)然蚕甥,對(duì)于框架類,我們可以簡(jiǎn)單的認(rèn)為栋荸,copy方法返回的就是不可變對(duì)象菇怀,mutableCopy返回的就是可變對(duì)象。如果是自定義的類晌块,就看你怎么實(shí)現(xiàn)NSCopying和NSMutableCopying協(xié)議了敏释。
4、copy和block
首先摸袁,MRR時(shí)代用retain修飾block會(huì)產(chǎn)生崩潰钥顽,因?yàn)樽鳛閷傩缘腷lock在初始化時(shí)是被存放在棧區(qū)或靜態(tài)區(qū)的,如果棧區(qū)的block內(nèi)調(diào)用外部變量靠汁,那么block無(wú)法保留其內(nèi)存蜂大,在初始化的作用域內(nèi)使用并不會(huì)有什么影響,但一旦出了block的初始化作用域蝶怔,就會(huì)引起崩潰奶浦。所有MRC中使用copy修飾,將block拷貝到堆上踢星。
其次澳叉,在ARC時(shí)代,因?yàn)锳RC自動(dòng)完成了對(duì)block的copy沐悦,所以修飾block用copy和strong都無(wú)所謂成洗。
5、strong和shallowCopy
這個(gè)問(wèn)題困惑了很久藏否,最后只能得出一個(gè)結(jié)論瓶殃,淺copy和strong引用的區(qū)別僅僅是淺copy多執(zhí)行一步copyWithZone:方法。
六副签、文獻(xiàn)