iOS对象复制(copy与mutableCopy)

 NSObject类提供了copymutableCopy方法,通过这两个方法即可复制已有对象的副本,本文将会详细介绍关于对象复制的内容。

系统对象的copy与mutableCopy

copy方法用于复制对象的副本。通常来说,copy方法总是返回对象的不可修改的副本,即使对象本身是可修改的。例如,程序调用NSMutableString的copy方法,将会返回不可修改的字符串对象,

mutableCopy方法用于复制对象的可变副本。通常来说,mutableCopy方法总是返回对象可修改的副本,即使被复制的对象本身是不可修改的,调用mutableCopy方法复制出来的副本也是可修改的。例如,程序调用NSString的mutableCopy方法,将会返回一个NSMutableString对象。

下图详细阐述了NSString、NSMutableString、NSArray、NSMutableArray、NSDictionary、NSMutableDictionary分别调用copy与mutableCopy方法后的结果:

系统对象复制

深复制与浅复制##

对象拷贝有两种方式:浅复制深复制。顾名思义,浅复制,并不拷贝对象本身,仅仅是拷贝指向对象的指针;深复制是直接拷贝整个对象内容到另一块内存中。再简单些说:浅复制就是指针拷贝;深复制就是内容拷贝

如果在多层数组中,对第一层进行内容拷贝,其它层进行指针拷贝,这种情况是属于深复制,还是浅复制?对此,苹果官网文档有这样一句话描述:

This kindofcopy is only capableofproducing a one-level-deep copy. If you only need a one-level-deep copy...If you need atruedeep copy, suchaswhen you have an arrayofarrays...

从文中可以看出,苹果认为这种复制不是真正的深复制,而是将其称为单层深复制(one-level-deep copy)。因此,有人对浅复制、完全深复制、单层深复制做了概念区分。当然,这些都是概念性的东西,没有必要纠结于此。只要知道进行拷贝操作时,被拷贝的是指针还是内容即可。

一般来说,完全深复制的实现难度大很多,尤其是当该对象包含大量的指针类型的实例变量时,如果某些实例变量里再次包含指针类型的实例变量,那么实现完全深复制会更加复杂。

上面图中的深复制(单层或者完全)就是因为集合对象中可能会包含指针类型的实例变量,从而导致深复制不完全。

自定义对象的复制##

使用copy和mutableCopy复制对象的副本使用起来确实方便,那么我们自定义的类是否可调用copy与mutableCopy方法来复制副本呢?

我们先定义一个SXYPerson类,代码如下:

@interfaceSXYPerson:NSObject

@property(nonatomic,assign)NSIntegerage;

@property(nonatomic,copy)NSString*name;

@end

然后尝试调用SXYPerson的copy方法来复制一个副本:

SXYPerson *person1 = [[SXYPerson alloc] init];//创建一个SXYPerson对象

person1.age =20;person1.name =@"苏小妖";

SXYPerson *person2 = [person1copy];//复制副本

运行程序,将会发生崩溃,并输出以下错误信息:

[SXYPerson copyWithZone:]:unrecognizedselectorsenttoinstance0x608000030920

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

我们将复制副本的代码换成如下:

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

再次运行程序,程序同样崩溃了,并输出去以下错误信息:

[SXYPerson mutableCopyWithZone:]:unrecognizedselectorsenttoinstance0x600000221120

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

大家可能会觉得疑惑,程序只是调用了copy和mutableCopy方法,为什么会提示找不到copyWithZone:与mutableCopyWithZone:方法呢?其实当程序调用对象的copy方法来复制自身时,底层需要调用copyWithZone:方法来完成实际的复制工作,copy返回实际上就是copyWithZone:方法的返回值;mutableCopy与mutableCopyWithZone:方法也是同样的道理。

那么怎么做才能让自定义的对象进行copy与mutableCopy呢?需要做以下事情:

1.让类实现NSCopying/NSMutableCopying协议。2.让类实现copyWithZone:/mutableCopyWithZone:方法

所以让我们的SXYPerson类能够复制自身,我们需要让SXYPerson实现NSCopying协议;然后实现copyWithZone:方法:

@interfaceSXYPerson:NSObject

@property(nonatomic,assign)NSIntegerage;

@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

运行之后发现我们实现了对象的复制:

自定义对象复制

同时需要注意的是如果对象中有其他指针类型的实例变量,且只是简单的赋值操作:person.obj2 = self.obj2,其中obj2是另一个自定义类,如果我们修改obj2中的属性,我们会发现复制后的person对象中obj2对象中的属性值也变了,因为对于这个对象并没有进行copy操作,这样的复制操作不是完全的复制,如果要实现完全的复制,需要将obj2对应的类也要实现copy,然后这样赋值:person.obj2 = [self.obj2 copy]。如果对象很多或者层级很多,实现起来还是很麻烦的。如果需要实现完全复制同样还有另有一种方法,那就是归档:

SXYPerson *person2 = [NSKeyedUnarchiverunarchiveObjectWithData:[NSKeyedArchiverarchivedDataWithRootObject:person1]];

这样我们就实现了自定义对象的复制,需要指出的是如果重写copyWithZone:方法时,其父类已经实现NSCopying协议,并重写过了copyWithZone:方法,那么子类重写copyWithZone:方法应先调用父类的copy方法复制从父类继承得到的成员变量,然后对子类中定义的成员变量进行赋值:

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

id obj = [super copyWithZone:zone];//对子类定义的成员变量赋值...

return obj;       

 }

关于mutableCopy的实现与copy的实现类似,只是实现的是NSMutableCopying协议与mutableCopyWithZone:方法。对于自定义的对象,在我看来并没有什么可变不可变的概念,因此实现mutableCopy其实是没有什么意义的,在此就不详细介绍了。

定义属性的copy指示符##

如下段代码,我们在定义属性的时候使用了copy指示符:

#import@interface SXYPerson:NSObject

@property(nonatomic,copy)NSMutableString*name;

@end

使用如下代码来进行测试:

SXYPerson *person1 = [[SXYPerson alloc] init];//创建一个SXYPerson对象

person1.name = [NSMutableString stringWithString:@"可变字符串"];

[person1.name appendString:@"123"];

运行程序会崩溃,并且提示以下信息:

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

这段错误提示不允许修改person的name属性,这是因为程序定义name属性时使用了copy指示符,该指示符置顶调用setName:方法时(通过点语法赋值时,实际上是调用对应的setter方法),程序实际上会使用参数的副本对name实际变量复制。也就是说,setName:方法的代码如下:

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

copy方法默认是复制该对象的不可变副本,虽然程序传入的NSMutableString,但程序调用该参数的copy方法得到的是不可变副本。因此,程序赋给SXYPerson对象的name实例变量的值依然是不可变字符串。

注意:定义合成getter、setter方法时并没有提供mutableCopy指示符。因此即使定义实例变量时使用了可变类型,但只要使用copy指示符,实例变量实际得到的值总是不可变对象。

总结

对于对象的深复制的概念没有必要那么纠结,只要我们理解了复制的本质,并且运用到我们的业务场景,选择我们想要的复制方式就可以。最主要的还是理解本质并且学会使用

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 1、系统对象的复制 不管是集合类对象,还是非集合类对象,接收到copy和mutableCopy消息时,都遵循以下准...
    Dwyane_Coding阅读 640评论 0 18
  • 先说结果下面会一一测试 1.NSString、NSMutableString、NSArray、NSMutableA...
    BLUEVIPIOS_阅读 695评论 1 3
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,065评论 1 32
  • 1、系统对象的复制 不管是集合类对象,还是非集合类对象,接收到copy和mutableCopy消息时,都遵循以下准...
    檀羽冲阅读 1,435评论 0 7
  • 1、去github上下载源代码 2、安装Maven 3、配置Maven 4、编译源码得到jar文件 github地...
    playman阅读 937评论 0 0