iOS开发-iOS中的拷贝

今天来说一下iOS中的copy

在iOS中,拷贝有两种方式,深拷贝(Deep copy)和浅拷贝(Shallow copy)。先上一张苹果官方的图来表示一下这两种拷贝。

copy1.png

可以看到,深拷贝是直接拷贝整个对象内存到另一块内存中。浅拷贝则是仅仅拷贝一份指向对象的指针,而并不拷贝对象本身。

有大佬总结的一句话就是,深拷贝就像克隆你的人,你人没有了,你的克隆人还在,而浅拷贝就像是克隆你的影子,你人如果没有了,那你的影子也就没有了。

但是上边说的深拷贝,其实也分成两种,一种是单层深拷贝(one-level-deep copy),还有一种是完全深拷贝(true deep copy)。

苹果官方有这样一句话:

This kind of copy is only capable of producing a one-level-deep copy. If you only need a one-level-deep copy, you can explicitly call for one as in Listing 2.

NSArray *deepCopyArray=[[NSArray alloc] initWithArray:someArray copyItems:YES];

If you need a true deep copy, such as when you have an array of arrays, you can archive and then unarchive the collection, provided the contents all conform to the NSCoding protocol. An example of this technique is shown in Listing 3.

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:

[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

说白了,单层深拷贝和完全深拷贝从名字就能看出来,只有一层是深复制,而另外一个是每一层都是深复制。

了解了深浅拷贝的概念,如何使用我们就不过多介绍了,官方文档里都有写的很清楚,那接下来我们来讨论一下开发中用到的各种类型的对象的拷贝吧。

首先我们先把我们平时用到的对象进行一个分类,分成集合类型、非集合类型和自定义类型,其中集合类型就是NSArray、NSDictionary这种,非集合类型就是NSString、NSNumber这种。

这些类型也都有对应的可变类型,比如NSMutableArray、NSMutableDictionary、NSMutableString...

而拷贝也分为两种,有mutableCopy和copy。

我们在集合类型中选出数组NSArray作为代表,非集合类型中选出NSString作为代表,来研究一下不同情况下的拷贝是深拷贝还是浅拷贝,以及拷贝出来的对象是什么类型。排列组合一下就有如下几种:

  • 集合类型 + 可变类型 + mutableCopy

  • 集合类型 + 可变类型 + copy

  • 集合类型 + 不可变类型 + mutableCopy

  • 集合类型 + 不可变类型 + copy

  • 非集合类型 + 可变类型 + mutableCopy

  • 非集合类型 + 可变类型 + copy

  • 非集合类型 + 不可变类型 + mutableCopy

  • 非集合类型 + 不可变类型 + copy

  • 自定义类型 + mutableCopy

  • 自定义类型 + copy

集合类型的拷贝

集合类型 + 可变类型

集合类型 + 可变类型 + mutableCopy

先上代码:


/**

 集合类型 - 可变类型 - mutableCopy

 */

- (void)setTypeMutableTypeMutableCopy

{

 NSMutableArray *mutableArr = [NSMutableArray arrayWithObjects:@"first",@"second",@"third", nil];

 NSMutableArray *mutableCopyMutableArr = [mutableArr mutableCopy];

 NSLog(@"%@",[[mutableArr mutableCopy] class]);

 NSLog(@"%@ --- %p",mutableArr,mutableArr);

 NSLog(@"%@ --- %p",mutableCopyMutableArr ,mutableCopyMutableArr);

}

打印结果:


2018-04-05 11:04:59.655093+0800 CopyDemo[11202:404553] __NSArrayM

2018-04-05 11:04:59.655338+0800 CopyDemo[11202:404553] (

 first,

 second,

 third

) --- 0x60000024c060

2018-04-05 11:04:59.655475+0800 CopyDemo[11202:404553] (

 first,

 second,

 third

) --- 0x60000024c5a0

可以看到结果,这种组合的情况下拷贝出的对象是一个可变数组(__NSArrayM),而两个打印出的数组的地址不同,所以是深拷贝。

集合类型 + 可变类型 + copy

先上代码:


/**

 集合类型 - 可变类型 - imutableCopy

 */

- (void)setTypeMutableTypeImutableCopy

{

 NSMutableArray *mutableArr = [NSMutableArray arrayWithObjects:@"first",@"second",@"third", nil];

 NSMutableArray *mutableCopyImutableArr = [mutableArr copy];

 NSLog(@"%@",[[mutableArr copy] class]);

 NSLog(@"%@ --- %p",mutableArr,mutableArr);

 NSLog(@"%@ --- %p",mutableCopyImutableArr ,mutableCopyImutableArr);

}

打印结果:


2018-04-05 11:04:59.655633+0800 CopyDemo[11202:404553] __NSArrayI

2018-04-05 11:04:59.655754+0800 CopyDemo[11202:404553] (

 first,

 second,

 third

) --- 0x604000250ef0

2018-04-05 11:04:59.655966+0800 CopyDemo[11202:404553] (

 first,

 second,

 third

) --- 0x604000251400

可以看到结果拷贝出的是一个不可变数组(__NSArrayI),另外两个数组的地址也不同,所以是深拷贝。

集合类型 + 不可变类型

集合类型 + 不可变类型 + mutableCopy

先上代码:


/**

 集合类型 - 不可变类型 - mutableCopy

 */

- (void)setTypeImutableTypeMutableCopy

{

 NSArray *imutableArr = [NSArray arrayWithObjects:@"first",@"second",@"third", nil];

 NSArray *imutableArrMutableCopy = [imutableArr mutableCopy];

 NSLog(@"%@",[[imutableArr mutableCopy] class]);

 NSLog(@"%@ --- %p",imutableArr,imutableArr);

 NSLog(@"%@ --- %p",imutableArrMutableCopy,imutableArrMutableCopy);

}

打印结果:


2018-04-05 11:04:59.656128+0800 CopyDemo[11202:404553] __NSArrayM

2018-04-05 11:04:59.656245+0800 CopyDemo[11202:404553] (

 first,

 second,

 third

) --- 0x604000251400

2018-04-05 11:04:59.656357+0800 CopyDemo[11202:404553] (

 first,

 second,

 third

) --- 0x604000251190

可以看到,拷贝出来的是一个可变数组,而且两个地址也不相同,为深拷贝。

集合类型 + 不可变类型 + copy

先上代码:


/**

 集合类型 - 不可变类型 - imutableCopy

 */

- (void)setTypeImutableTypeImutableCopy

{

 NSArray *imutableArr = [NSArray arrayWithObjects:@"first",@"second",@"third", nil];

 NSArray *imutableArrImutableCopy = [imutableArr copy];

 NSLog(@"%@",[[imutableArr copy] class]);

 NSLog(@"%@ --- %p",imutableArr,imutableArr);

 NSLog(@"%@ --- %p",imutableArrImutableCopy,imutableArrImutableCopy);

}

打印结果:


2018-04-05 11:04:59.656530+0800 CopyDemo[11202:404553] __NSArrayI

2018-04-05 11:04:59.656685+0800 CopyDemo[11202:404553] (

 first,

 second,

 third

) --- 0x60000024c5a0

2018-04-05 11:04:59.656809+0800 CopyDemo[11202:404553] (

 first,

 second,

 third

) --- 0x60000024c5a0

这个结果好像和之前的有点不同,两个地址是相同的,所以为浅拷贝,另外拷贝出来的是一个不可变数组。

集合类型总结

| |可变类型|不可变类型

---|---|---

mutableCopy| 可变类型,深拷贝|可变类型,深拷贝 |

copy|不可变类型,深拷贝|不可变类型,浅拷贝|

非集合类型的拷贝

非集合类型中的拷贝出来的类型结果好像有点不对,这里暂时没找出原因,如果有大佬看到希望能够帮忙指正一下。

非集合类型 + 可变类型

非集合类型 + 可变类型 + mutableCopy

先上代码:


#pragma mark -

#pragma mark - not set type

/**

 非集合类型 - 可变类型 - mutableCopy

 */

- (void)unsetTypeMutableTypeMutableCopy

{

 NSMutableString *mutableStr = [NSMutableString stringWithFormat:@"mutableStr"];

 NSMutableString *mutableStrMutableCopy = [mutableStr mutableCopy];

 NSLog(@"%@",[[mutableStr mutableCopy] class]);

 NSLog(@"%@ --- %p",mutableStr,mutableStr);

 NSLog(@"%@ --- %p",mutableStrMutableCopy,mutableStrMutableCopy);

}

打印结果:


2018-04-05 11:04:59.656967+0800 CopyDemo[11202:404553] __NSCFString

2018-04-05 11:04:59.657094+0800 CopyDemo[11202:404553] mutableStr --- 0x604000251190

2018-04-05 11:04:59.657223+0800 CopyDemo[11202:404553] mutableStr --- 0x604000251550

可以看到地址不同,深拷贝。

非集合类型 + 可变类型 + copy

先上代码:


/**

 非集合类型 - 可变类型 - imutableCopy

 */

- (void)unsetTypeMutableTypeImutableCopy

{

 NSMutableString *mutableStr = [NSMutableString stringWithFormat:@"mutableStr"];

 NSMutableString *mutableStrImutableCopy = [mutableStr copy];

 NSLog(@"%@",[[mutableStr copy] class]);

 NSLog(@"%@ --- %p",mutableStr,mutableStr);

 NSLog(@"%@ --- %p",mutableStrImutableCopy,mutableStrImutableCopy);

}

打印结果:


2018-04-05 11:04:59.661548+0800 CopyDemo[11202:404553] __NSCFString

2018-04-05 11:04:59.661682+0800 CopyDemo[11202:404553] mutableStr --- 0x604000251550

2018-04-05 11:04:59.661850+0800 CopyDemo[11202:404553] mutableStr --- 0x60400003c580

可以看到地址不同,深拷贝。

非集合类型 + 不可变类型

非集合类型 + 不可变类型 + mutableCopy

代码:


/**

 非集合类型 - 不可变类型 - mutableCopy

 */

- (void)unsetTypeImutableTypeMutableCopy

{

 NSString *str = [NSString stringWithFormat:@"ImutableStr"];

 NSString *strMutableCopy = [str mutableCopy];

 NSLog(@"%@",[[str mutableCopy] class]);

 NSLog(@"%@ --- %p",str,str);

 NSLog(@"%@ --- %p",strMutableCopy,strMutableCopy);

}

打印结果:


2018-04-05 11:04:59.661968+0800 CopyDemo[11202:404553] __NSCFString

2018-04-05 11:04:59.662072+0800 CopyDemo[11202:404553] ImutableStr --- 0x60400003c580

2018-04-05 11:04:59.662184+0800 CopyDemo[11202:404553] ImutableStr --- 0x604000251340

同样是深拷贝,地址不同。

非集合类型 + 不可变类型 + copy

先上代码:


/**

 非集合类型 - 不可变类型 - imutableCopy

 */

- (void)unsetTypeImutableTypeImutableCopy

{

 NSString *str = [NSString stringWithFormat:@"ImtableStr"];

 NSString *strImutableCopy = [str copy];

 NSLog(@"%@",[[str copy] class]);

 NSLog(@"%@ --- %p",str,str);

 NSLog(@"%@ --- %p",strImutableCopy,strImutableCopy);

}

打印结果:


2018-04-05 11:04:59.662703+0800 CopyDemo[11202:404553] __NSCFString

2018-04-05 11:04:59.663681+0800 CopyDemo[11202:404553] ImtableStr --- 0x600000237340

2018-04-05 11:04:59.663882+0800 CopyDemo[11202:404553] ImtableStr --- 0x600000237340

可以看到,地址是相同的,所以为浅拷贝。

非集合类型总结

所以,可以发现,集合与非集合类型的结果基本上相同,但是这里拷贝出的类型好像有点问题。

| |可变类型|不可变类型

---|---|---

mutableCopy| 深拷贝|深拷贝 |

copy|深拷贝|浅拷贝|

自定义类型

我们先自定义一个model,里边包括一个name,一个location。

注意这里要添加NSCopying和NSMutableCopying协议。


#import <Foundation/Foundation.h>

@interface CopyModel : NSObject<NSCopying,NSMutableCopying>

@property (nonatomic ,copy) NSString *name;

@property (nonatomic ,copy) NSString *location;

@end

然后还需要实现两个方法,否则在copy或mutableCopy时会发生崩溃。


#import "CopyModel.h"

@implementation CopyModel

- (instancetype)copyWithZone:(NSZone *)zone

{

 CopyModel *copyModel = [[CopyModel alloc] init];

 copyModel.name = self.name;

 copyModel.location = self.location;

 return copyModel;

}

- (instancetype)mutableCopyWithZone:(NSZone *)zone

{

 CopyModel *copyModel = [[CopyModel alloc] init];

 copyModel.name = self.name;

 copyModel.location = self.location;

 return copyModel;

}

@end

接下来我们来看一下自定义类型的copy和mutableCopy的结果。

自定义类型 + mutableCopy

代码:


/**

 自定义类型 - mutableCopy

 */

- (void)customTypeMutableCopy

{

 CopyModel *model = [[CopyModel alloc] init];

 model.name = @"name";

 model.location = @"location";

 CopyModel *copyModel = [model mutableCopy];

 NSLog(@"%@ --- %@ --- %p",model.name,model.location,model);

 NSLog(@"%@ --- %@ --- %p",copyModel.name,copyModel.location,copyModel);

}

打印结果:


2018-04-05 11:04:59.664868+0800 CopyDemo[11202:404553] name --- location --- 0x60400003c400

2018-04-05 11:04:59.665014+0800 CopyDemo[11202:404553] name --- location --- 0x60400003c320

可以看到地址不同,是深拷贝。

自定义类型 + copy

代码:


/**

 自定义类型 - ImutableCopy

 */

- (void)customTypeImutableCopy

{

 CopyModel *model = [[CopyModel alloc] init];

 model.name = @"name";

 model.location = @"location";

 CopyModel *copyModel = [model copy];

 NSLog(@"%@ --- %@ --- %p",model.name,model.location,model);

 NSLog(@"%@ --- %@ --- %p",copyModel.name,copyModel.location,copyModel);

}

打印结果:


2018-04-05 11:04:59.665165+0800 CopyDemo[11202:404553] name --- location --- 0x60400003c400

2018-04-05 11:04:59.665404+0800 CopyDemo[11202:404553] name --- location --- 0x60400003c320

可以看到地址不同,是深拷贝。

@property 中的copy

属性的常用修饰符里,就有一个就是copy,那这个copy和其他的修饰符会有什么区别呢?

copy vs strong

一般在修饰NSString属性时,通常会用copy,那如果我们不用copy,而改用strong会发生什么问题呢??

先上代码:


@property (nonatomic ,strong) NSString *myStrongStr;

@property (nonatomic ,copy) NSString *myCopyStr;


- (void)strongStrVSCopyStr

{

 NSMutableString *otherStr = [NSMutableString stringWithFormat:@"otherStr"];

 self.myStrongStr = otherStr;

 self.myCopyStr = otherStr;

 [otherStr appendString:@"addSomeThing"];

 NSLog(@"%@ --- %p",self.myStrongStr,self.myStrongStr);

 NSLog(@"%@ --- %p",self.myCopyStr,self.myCopyStr);

 NSLog(@"%@ --- %p",otherStr,otherStr);

}

打印结果:


2018-04-05 11:34:04.034151+0800 CopyDemo[11944:480031] otherStraddSomeThing --- 0x600000241170

2018-04-05 11:34:04.034339+0800 CopyDemo[11944:480031] otherStr --- 0xa000c45401541058

2018-04-05 11:34:04.034619+0800 CopyDemo[11944:480031] otherStraddSomeThing --- 0x600000241170

结果应该很明显了,我并没有对myStrongStr进行操作,但是他的结果却变了,而且他与otherStr的地址也是相同的,那这是为什么呢?

是因为copy修饰的属性setter方法,调用时会先release旧值,copy新值再赋值给成员变量,不可变copy是深拷贝,地址就变化了。

而strong修饰之后只是强指针引用,并没有改变地址,所以myStrongStr会随着otherStr的值进行变化。他们的地址也是相同的。

copy vs retain

在MRC下进行测试:


- (void)strongVSretain {

 NSMutableArray *arrM = [NSMutableArray arrayWithObjects:@"111",@"222",@"333", nil];

 NSMutableArray *arrMRetain = [arrM retain];

 NSMutableArray *arrMCopy = [arrM copy];

 [arrM removeLastObject];

 NSLog(@"arrMCopy--%@--%p--%lu",arrMCopy,arrMCopy,[arrMCopy retainCount]);

 NSLog(@"arrMRetain--%@--%p--%lu",arrMRetain,arrMRetain,[arrMRetain retainCount]);

}

打印结果:


2018-04-05 11:34:04.034619+0800 CopyDemo[11944:480031] arrMCopy--(

 111,

 222,

 333

)--0x60000005cf80--1

2018-04-05 11:34:04.034619+0800 CopyDemo[11944:480031] arrMRetain--(

 111,

 222

)--0x60000005cf50--2

可以看到,copy是深拷贝,retainCount为1,retain为浅拷贝,retain是使原来的引用计数+1,所以两个数组时同一个地址,改变的话也会一起改变。

可变类型使用copy修饰

当我们创建一个可变类型的变量而使用copy修饰的时候,可能会发生一个问题,就是使用可变类型的方法时,就会发生崩溃。

比如:


@property (nonatomic ,copy) NSMutableArray *myMutableArr;


 self.myMutableArr = [NSMutableArray arrayWithObjects:@"first",@"second",@"third", nil];

 [self.myMutableArr addObject:@"forth"];

这段代码运行时就会发生崩溃,报错信息为:


2018-04-05 11:42:52.239877+0800 CopyDemo[12135:500454] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x600000250200'

我们发现这个报错的原因为不可变数组(__NSArrayI)找不到addObject:的方法。那是为什么呢?

因为我们之前也说过,在使用copy修饰符的时候,是先release掉旧值,再copy出一个新的值,而在前边的内容中也有说到,可变数组copy出来的是一个不可变数组,所以实际上这时候myMutableArr就是一个不可变数组了,这时候给他发送可变数组的方法,他当然就会崩溃了。

最后

最后总结一下,可以看到,只有在不可变类型的copy时,才会发生浅拷贝,而其他所有的情况下都为深拷贝,并且一般来说无论是可变还是不可变类型的变量,mutableCopy后都会变成可变类型,copy后都会变成不可变类型。

最后Demo在这里

祝大家清明节快乐~~

参考文档

Copying Collections

iOS 集合的深复制与浅复制

ios中的拷贝你知道多少?

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

推荐阅读更多精彩内容