【轉(zhuǎn)】iOS之對象復制

前言##

NSObject類提供了copymutableCopy方法,通過這兩個方法即可復制已有對象的副本,本文將會詳細介紹關(guān)于對象復制的內(nèi)容。

系統(tǒng)對象的copy與mutableCopy##

copy方法用于復制對象的副本。通常來說,copy方法總是返回對象的不可修改的副本,即使對象本身是可修改的。例如,程序調(diào)用NSMutableString的copy方法,將會返回不可修改的字符串對象,

mutableCopy方法用于復制對象的可變副本。通常來說,mutableCopy方法總是返回對象可修改的副本,即使被復制的對象本身是不可修改的,調(diào)用mutableCopy方法復制出來的副本也是可修改的。例如,程序調(diào)用NSString的mutableCopy方法,將會返回一個NSMutableString對象。

下圖詳細闡述了NSString、NSMutableString、NSArray、NSMutableArray、NSDictionary、NSMutableDictionary分別調(diào)用copy與mutableCopy方法后的結(jié)果:

系統(tǒng)對象復制

深復制與淺復制##

對象拷貝有兩種方式:淺復制深復制。顧名思義,淺復制,并不拷貝對象本身,僅僅是拷貝指向?qū)ο蟮闹羔?;深復制是直接拷貝整個對象內(nèi)容到另一塊內(nèi)存中。再簡單些說:淺復制就是指針拷貝;深復制就是內(nèi)容拷貝。

如果在多層數(shù)組中,對第一層進行內(nèi)容拷貝,其它層進行指針拷貝,這種情況是屬于深復制,還是淺復制?對此,蘋果官網(wǎng)文檔有這樣一句話描述:

This kind ofcopyis only capable of producing a one-level-deepcopy.If you only need a one-level-deepcopy...If you need atruedeepcopy,such as when you have an array of arrays...

從文中可以看出,蘋果認為這種復制不是真正的深復制,而是將其稱為單層深復制(one-level-deep copy)。因此,有人對淺復制、完全深復制、單層深復制做了概念區(qū)分。當然,這些都是概念性的東西,沒有必要糾結(jié)于此。只要知道進行拷貝操作時,被拷貝的是指針還是內(nèi)容即可。

一般來說,完全深復制的實現(xiàn)難度大很多,尤其是當該對象包含大量的指針類型的實例變量時,如果某些實例變量里再次包含指針類型的實例變量,那么實現(xiàn)完全深復制會更加復雜。

上面圖中的深復制(單層或者完全)就是因為集合對象中可能會包含指針類型的實例變量,從而導致深復制不完全。

自定義對象的復制##

使用copy和mutableCopy復制對象的副本使用起來確實方便,那么我們自定義的類是否可調(diào)用copy與mutableCopy方法來復制副本呢?

我們先定義一個SXYPerson類,代碼如下:

@interfaceSXYPerson:NSObject@property(nonatomic,assign)NSInteger age;@property(nonatomic,copy)NSString*name;@end

然后嘗試調(diào)用SXYPerson的copy方法來復制一個副本:

SXYPerson*person1=[[SXYPerson alloc]init];//創(chuàng)建一個SXYPerson對象person1.age=20;person1.name=@"蘇小妖";SXYPerson*person2=[person1copy];//復制副本

運行程序,將會發(fā)生崩潰,并輸出以下錯誤信息:

[SXYPersoncopyWithZone:]:unrecognized selector sent to instance0x608000030920

上面的提示:SXYPerson找不到copyWithZone:方法。

我們將復制副本的代碼換成如下:

SXYPerson*person2=[person1 mutableCopy];//復制副本

再次運行程序,程序同樣崩潰了,并輸出去以下錯誤信息:

[SXYPersonmutableCopyWithZone:]:unrecognized selector sent to instance0x600000221120

上面的提示:SXYPerson找不到mutableCopyWithZone:方法。

大家可能會覺得疑惑,程序只是調(diào)用了copy和mutableCopy方法,為什么會提示找不到copyWithZone:與mutableCopyWithZone:方法呢?其實當程序調(diào)用對象的copy方法來復制自身時,底層需要調(diào)用copyWithZone:方法來完成實際的復制工作,copy返回實際上就是copyWithZone:方法的返回值;mutableCopy與mutableCopyWithZone:方法也是同樣的道理。

那么怎么做才能讓自定義的對象進行copy與mutableCopy呢?需要做以下事情:

1.讓類實現(xiàn)NSCopying/NSMutableCopying協(xié)議。2.讓類實現(xiàn)copyWithZone:/mutableCopyWithZone:方法

所以讓我們的SXYPerson類能夠復制自身,我們需要讓SXYPerson實現(xiàn)NSCopying協(xié)議;然后實現(xiàn)copyWithZone:方法:

@interfaceSXYPerson:NSObject<NSCopying>@property(nonatomic,assign)NSInteger age;@property(nonatomic,copy)NSString*name;@end

#import"SXYPerson.h"@implementationSXYPerson-(id)copyWithZone:(NSZone*)zone{SXYPerson*person=[[[selfclass]allocWithZone:zone]init];person.age=self.age;person.name=self.name;returnperson;}@end

運行之后發(fā)現(xiàn)我們實現(xiàn)了對象的復制:

自定義對象復制

同時需要注意的是如果對象中有其他指針類型的實例變量,且只是簡單的賦值操作:person.obj2 = self.obj2,其中obj2是另一個自定義類,如果我們修改obj2中的屬性,我們會發(fā)現(xiàn)復制后的person對象中obj2對象中的屬性值也變了,因為對于這個對象并沒有進行copy操作,這樣的復制操作不是完全的復制,如果要實現(xiàn)完全的復制,需要將obj2對應的類也要實現(xiàn)copy,然后這樣賦值:person.obj2 = [self.obj2 copy]。如果對象很多或者層級很多,實現(xiàn)起來還是很麻煩的。如果需要實現(xiàn)完全復制同樣還有另有一種方法,那就是歸檔:

SXYPerson*person2=[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:person1]];

這樣我們就實現(xiàn)了自定義對象的復制,需要指出的是如果重寫copyWithZone:方法時,其父類已經(jīng)實現(xiàn)NSCopying協(xié)議,并重寫過了copyWithZone:方法,那么子類重寫copyWithZone:方法應先調(diào)用父類的copy方法復制從父類繼承得到的成員變量,然后對子類中定義的成員變量進行賦值:

-(id)copyWithZone:(NSZone*)zone{id obj=[supercopyWithZone:zone];//對子類定義的成員變量賦值...returnobj;}

關(guān)于mutableCopy的實現(xiàn)與copy的實現(xiàn)類似,只是實現(xiàn)的是NSMutableCopying協(xié)議與mutableCopyWithZone:方法。對于自定義的對象,在我看來并沒有什么可變不可變的概念,因此實現(xiàn)mutableCopy其實是沒有什么意義的,在此就不詳細介紹了。

定義屬性的copy指示符##

如下段代碼,我們在定義屬性的時候使用了copy指示符:

#import@interfaceSXYPerson:NSObject<NSCopying>@property(nonatomic,copy)NSMutableString*name;@end

使用如下代碼來進行測試:

SXYPerson*person1=[[SXYPersonalloc]init];//創(chuàng)建一個SXYPerson對象person1.name=[NSMutableStringstringWithString:@"蘇小妖"];[person1.name appendString:@"123"];

運行程序會崩潰,并且提示以下信息:

***Terminating app due to uncaught exception'NSInvalidArgumentException',reason:'Attempt to mutate immutable object with appendString:'

這段錯誤提示不允許修改person的name屬性,這是因為程序定義name屬性時使用了copy指示符,該指示符置頂調(diào)用setName:方法時(通過點語法賦值時,實際上是調(diào)用對應的setter方法),程序?qū)嶋H上會使用參數(shù)的副本對name實際變量復制。也就是說,setName:方法的代碼如下:

-(void)setName:(NSMutableString*)name{_name=[name copy];}

copy方法默認是復制該對象的不可變副本,雖然程序傳入的NSMutableString,但程序調(diào)用該參數(shù)的copy方法得到的是不可變副本。因此,程序賦給SXYPerson對象的name實例變量的值依然是不可變字符串。

注意:定義合成getter、setter方法時并沒有提供mutableCopy指示符。因此即使定義實例變量時使用了可變類型,但只要使用copy指示符,實例變量實際得到的值總是不可變對象。

總結(jié)##

對于對象的深復制的概念沒有必要那么糾結(jié),只要我們理解了復制的本質(zhì),并且運用到我們的業(yè)務場景,選擇我們想要的復制方式就可以。最主要的還是理解本質(zhì)并且學會使用。

參考鏈接:http://www.itdecent.cn/p/ac07c26f467d

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

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

  • NSObject類提供了copy和mutableCopy方法,通過這兩個方法即可復制已有對象的副本,本文將會詳細介...
    Davis_閱讀 3,445評論 0 4
  • 1、系統(tǒng)對象的復制 不管是集合類對象,還是非集合類對象,接收到copy和mutableCopy消息時,都遵循以下準...
    Dwyane_Coding閱讀 684評論 0 18
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,621評論 1 32
  • 1、系統(tǒng)對象的復制 不管是集合類對象,還是非集合類對象,接收到copy和mutableCopy消息時,都遵循以下準...
    檀羽沖閱讀 1,472評論 0 7
  • 上個星期二的一節(jié)語文課,到了下課的時候,董老師莊重的 對我們說。:“同學們,明天我要出去學習,接下來我先給你們布置...
    楊靈恩閱讀 350評論 0 0

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