iOS开发-对象拷贝

NSObject类提供了两个用于拷贝的方法:- (id)copy 和- (id)mutableCopy,这两个方法都可以复制已有对象的副本。由于oc中几乎所有的类都继承自根类NSObject,所以类中都有copy和mutableCopy两个方法,那么是否就意味着拥有这两个方法的对象可以直接调用这两个方法进行拷贝了呢?

我们先定义一个AMPerson类继承自NSObject 进行测试,代码如下:

AMPerson *p1 = [[AMPerson alloc] init];

AMPerson *p2 = [p1 copy];

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

-[AMPerson copyWithZone:]: unrecognized selector sent to instance 0x7bf5e880  意思是:AMPerson类中找不到copyWithZone:方法。

把copy方法换成mutableCopy,AMPerson *p2 = [p1 mutableCopy],运行之后,依然发生崩溃,提示信息如下:

-[AMPerson mutableCopyWithZone:]: unrecognized selector sent to instance 0x7a2415f0 意思是:AMPerson类中找不到mutableCopyWithZone:方法。

由以上错误可知:拷贝操作表面是调用copy和mutableCopy方法,其实底层是调用对象自身的copyWithZone和mutableCopyWithZone方法来完成实际的复制工作。copy返回实际上就是copyWithZone:方法的返回值;mutableCopy与mutableCopyWithZone:方法也是同样的道理。

由该例就引出了下面的讨论内容了。就是对象具体要满足什么条件,才可以被复制。


分为两大类来讨论:首先,自定义对象的复制。

要想自定义对象可以复制,那么该类就必须

一,遵守NSCopying 或 NSMutableCopying协议。

二,实现协议中copyWithZone或者mutableCopyWithZone方法。

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

@interface AMPerson : NSObject   <NSCopying>

@property (copy,nonatomic)NSString *name;

@end

@implementation AMPerson

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

 AMPerson *p = [[[self class] allocWithZone:zone] init];

p.name = [self.name copy];

 return p;

}

@end

然后再运行这两句代码

AMPerson *p1 = [[AMPerson alloc] init];

AMPerson *p2 = [p1 copy];

NSLog(@"p1 = %p,p2 = %p",p1,p2);

打印结果为:p1 = 0x7969cc40,p2 = 0x7969c6e0

结果表明:p1和p2是两个地址不同的不同对象,复制操作成功。


其次,再来讨论系统的对象的复制

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

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

下图详细列出了NSString、NSMutableString、NSArray、NSMutableArray、NSDictionary、NSMutableDictionary等分别调用copy与mutableCopy方法后的结果。

系统对象复制

深复制与浅复制

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

接着,我们来讨论一下多层数组的复制。

如果在多层数组中,对第一层进行内容拷贝,其它层进行指针拷贝,这种情况是属于深复制,还是浅复制?

如下所示

AMPerson *p1 = [[AMPerson alloc] init];

AMPerson *p2 = [[AMPerson alloc] init];

AMPerson *p3 = [[AMPerson alloc] init];

NSArray *p = @[p1,p2,p3];//数组

NSArray *pCopy = [p mutableCopy];//复制

NSLog(@"p = %p,pCopy = %p",p,pCopy); //打印结果: p = 0x7c268090,pCopy = 0x7c26af20

NSLog(@"p = %p,pCopy = %p",p[0],pCopy[0]); //打印结果:p = 0x7c26a6e0,pCopy = 0x7c26a6e0

打印结果表明:数组复制只是单单对于数组对象本身而言是深复制,而数组的成员对象默认仍然是浅拷贝的。我们称之为单层深复制。

那么要想实现完全深复制该怎么办呢? 尤其是当该对象包含大量的指针类型的实例变量时,如果某些实例变量里再次包含指针类型的实例变量,那么实现完全深复制会更加复杂。上面的深复制就是因为集合对象中可能会包含指针类型的实例变量,从而导致深复制不完全。

把上面的复制代码NSArray *pCopy = [p mutableCopy]换成NSArray *pCopy = [[NSMutableArray alloc] initWithArray:p copyItems:YES]即可。

AMPerson *p1 = [[AMPerson alloc] init];

AMPerson *p2 = [[AMPerson alloc] init];

AMPerson *p3 = [[AMPerson alloc] init];

NSArray *p = @[p1,p2,p3];

NSArray *pCopy = [[NSMutableArray alloc] initWithArray:p copyItems:YES];//复制

NSLog(@"p = %p,pCopy = %p",p,pCopy);//打印结果:p = 0x7a93af60,pCopy = 0x7a939680

NSLog(@"p = %p,pCopy = %p",p[0],pCopy[0]);//打印结果:p = 0x7a93c2e0,pCopy = 0x7a93c990

结果表明这次的复制是 完全深复制。不仅仅复制了第一层的数组对象,也复制了数组内部的指针类型的实例变量。当然内部的实例变量要遵守NSCoping协议。

总结

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

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

推荐阅读更多精彩内容

  • 本文为转载: 作者:zyydeveloper 链接:http://www.jianshu.com/p/5f776a...
    Buddha_like阅读 852评论 0 2
  • 1、对象拷贝有两种方式:浅复制和深复制。顾名思义,浅复制,并不拷贝对象本身,仅仅是拷贝指向对象的指针;深复制是直接...
    滴答大阅读 756评论 0 2
  • 为什么很多内置类如UITableView的delegate属性都是assign而不是retain? 所有的引用计数...
    烟雨平生花飞舞阅读 1,190评论 0 3
  • 早晨醒来,难得地有一种清净自在的感觉。 我犹豫了一下,没有向往常一样第一时间打开手机看推送,隐隐地觉得只要一打开,...
    Tina_Sun阅读 298评论 0 7
  • 美好的生活 有人向往别墅豪车,有人向往海浪沙滩,有人向往都市繁华。我脑海中总是向往这样的生活:青山绿水旁,三间平房...
    荷蓉阅读 621评论 8 10