iOS深拷贝和浅拷贝

在工作中,有时会涉及到深拷贝和浅拷贝的内容,发现有些地方理解的不够透彻,所以在网上搜集资料总结一下,主要分四个方面来介绍下iOS中深拷贝和浅拷贝:

  • 对象的拷贝;
  • 集合的拷贝;
  • 如何对集合进行深拷贝?
  • 总结

对象的拷贝

对对象进行拷贝,通过调用copymutableCopy方法来实现:

  • 调用 copy方法返回的对象为不可变对象,需要实现NSCopying协议 ;
  • 调用mutableCopy方法返回的对象为可变对象,需要实现NSMutableCopying协议 ;
object copying

上图是苹果文档中关于对象拷贝的实例图,从图中可知:

浅拷贝:object Aobject B及其属性使用同样的内存地址,简单说明的话,可以认为是指针复制;
深拷贝:object Aobject B及其属性使用不同的内存地址,简单说明的话,可以认为是内容复制。

下面通过分析NSStringNSMutableString、和自定义对象DBTestModel,调用copymutableCopy之后,分析其返回的对象及此对象的属性的内存地址来判断其行为是深拷贝还是浅拷贝。

NSString
NSString.png

通过打印的信息可知:

  • 对象string调用copy方法后返回的对象copySting,其所对应的内存地址和对象string一致,即为指针复制;
  • 对象string调用mutableCopy方法后返回的对象mutaCopySting,其所对应的内存地址和对象string不一致,即为指内容复制;
NSMutableString
NSMutableString.png

通过打印的信息可知:

  • 对象mutaString调用copy方法后返回的对象copyMutaString,其所对应的内存地址和对象mutaString不一致,即为内容复制;
  • 对象mutaString调用mutableCopy方法后返回的对象mutaCopyMutaString,其所对应的内存地址和对象mutaString不一致,即为指内容复制;
DBTestModel

下面为自定义的对象'DBTestModel':

#import <Foundation/Foundation.h>
#import <Mantle/Mantle.h>

@interface DBTestModel : MTLModel

@property (nonatomic, copy) NSString *text;

@property (nonatomic, strong) NSArray *sourceArray;

@property (nonatomic, strong) NSMutableArray *mutableArray;

- (instancetype)initWithText:(NSString *)text
                 sourceArray:(NSArray *)sourceArray
                mutableArray:(NSMutableArray *)mutableArray;
@end

定义了三个属性,textsourceArraymutableArray

- (void)testCustomObject {
    NSMutableArray *mutableArray = [NSMutableArray array];
    DBTestModel *model = [[DBTestModel alloc] initWithText:@"text"
                                               sourceArray:@[@"test1",@"test2"]
                                              mutableArray:mutableArray];
    DBTestModel *copyModel = [model copy];
    DBTestModel *mutaCopyModel = [model mutableCopy];
    
    NSLog(@"DBTestModel memory address");
    NSLog(@"original   :%p", model);
    NSLog(@"copy       :%p", copyModel);
    NSLog(@"mutableCopy:%p", mutaCopyModel);
    NSLog(@"\n");
    
    NSLog(@"text memory address");
    NSLog(@"original   :%p", model.text);
    NSLog(@"copy       :%p", copyModel.text);
    NSLog(@"mutableCopy:%p", mutaCopyModel.text);
    NSLog(@"\n");
    
    NSLog(@"sourceArray memory address");
    NSLog(@"original   :%p", model.sourceArray);
    NSLog(@"copy       :%p", copyModel.sourceArray);
    NSLog(@"mutableCopy:%p", mutaCopyModel.sourceArray);
    NSLog(@"\n");
    
    NSLog(@"mutableArray memory address");
    NSLog(@"original   :%p", model.mutableArray);
    NSLog(@"copy       :%p", copyModel.mutableArray);
    NSLog(@"mutableCopy:%p", mutaCopyModel.mutableArray);
    NSLog(@"\n"); 
}

打印结果如下:


image.png

分析其打印数据可知:

  • DBTestModel实例对象中的属性textsourceArray调用copy后,没有产生一个新的对象,为指针复制,其余均为内容复制。

集合的拷贝

image.png
不可变集合NSArray
- (void)testCollectiveCopy {
    NSMutableArray *mutableArray1 = [NSMutableArray array];
    DBTestModel *model1 = [[DBTestModel alloc] initWithText:@"text"
                                               sourceArray:@[@"test1",@"test2"]
                                              mutableArray:mutableArray1];
    
    NSMutableArray *mutableArray2 = [NSMutableArray array];
    DBTestModel *model2 = [[DBTestModel alloc] initWithText:@"text"
                                                sourceArray:@[@"test1",@"test2"]
                                               mutableArray:mutableArray2];
    
    NSMutableArray *mutableArray3 = [NSMutableArray array];
    DBTestModel *model3 = [[DBTestModel alloc] initWithText:@"text"
                                                sourceArray:@[@"test1",@"test2"]
                                               mutableArray:mutableArray3];
    NSArray *array = @[model1,model2,model3];
    NSArray *copyArray = [array copy];
    NSMutableArray *mutaCopyArray = [array mutableCopy];

    NSLog(@"\nNSArray memory address\noriginal   :%p\ncopy       :%p\nmutableCopy:%p\n",
          array,copyArray,mutaCopyArray);
 
    DBTestModel *firstCopyModel = [copyArray firstObject];
    DBTestModel *firstMutableCopyModel = [mutaCopyArray firstObject];
    NSLog(@"\nDBTestModel memory address\noriginal   :%p\ncopy       :%p\nmutableCopy:%p\n",
          model1,firstCopyModel,firstMutableCopyModel);
 
    NSLog(@"\nDBTestModel sourceArray memory address\noriginal   :%p\ncopy       :%p\nmutableCopy:%p\n",
          model1.sourceArray,firstCopyModel.sourceArray,firstMutableCopyModel.sourceArray); 
}

打印结果如下:

NSArray.png

分析其打印数据可知:

  • NSArray实例对象调用mutableCopy方法为内容复制外,其余的均为指针拷贝。
可变集合NSMutableArray

测试代码如下:

- (void)testCollectiveMutacopy {
    NSMutableArray *mutableArray1 = [NSMutableArray array];
    DBTestModel *model1 = [[DBTestModel alloc] initWithText:@"text"
                                                sourceArray:@[@"test1",@"test2"]
                                               mutableArray:mutableArray1];
    
    NSMutableArray *mutableArray2 = [NSMutableArray array];
    DBTestModel *model2 = [[DBTestModel alloc] initWithText:@"text"
                                                sourceArray:@[@"test1",@"test2"]
                                               mutableArray:mutableArray2];
    
    NSMutableArray *mutableArray3 = [NSMutableArray array];
    DBTestModel *model3 = [[DBTestModel alloc] initWithText:@"text"
                                                sourceArray:@[@"test1",@"test2"]
                                               mutableArray:mutableArray3];
    
    NSArray *array1 = @[model1,model2,model3];
    NSMutableArray *mutaArray = [NSMutableArray arrayWithArray:array1];
    NSMutableArray *copyMutaArray = [mutaArray copy];
    NSMutableArray *mutaCopyMutaArray= [mutaArray mutableCopy];
    
    NSLog(@"\nNSMutableArray memory address\noriginal   :%p\ncopy       :%p\nmutableCopy:%p\n",
          mutaArray,copyMutaArray,mutaCopyMutaArray);
    
    DBTestModel *firstCopyModel = [copyMutaArray firstObject];
    DBTestModel *firstMutableCopyModel = [mutaCopyMutaArray firstObject];
    NSLog(@"\nDBTestModel memory address\noriginal   :%p\ncopy       :%p\nmutableCopy:%p\n",
          model1,firstCopyModel,firstMutableCopyModel);
    
    NSLog(@"\nDBTestModel sourceArray memory address\noriginal   :%p\ncopy       :%p\nmutableCopy:%p\n",
          model1.sourceArray,firstCopyModel.sourceArray,firstMutableCopyModel.sourceArray);
}

测试结果如下:

NSMutableArray.png

分析其打印数据可知:
NSMutableArray实例对象调用copymutableCopy方法为内容复制外,数组内容的元素均为指针拷贝。
结合上述测试代码得出的测试数据,得出如下的表格:

image.png

从上图可以看出,NSArrayNSMutableArray对象调用copymutableCopy时,得到的集合中的元素均为指针拷贝,如果想要实现集合对象的深拷贝,应该怎么办呢?

  • 如何对集合进行深拷贝?

  • 集合的单层深复制 (one-level-deep copy)

可以用 initWithArray:copyItems: 将第二个参数设置为YES即可深复制,如

 NSArray *copyArray = [[NSArray alloc] initWithArray:array copyItems:YES];

如果你用这种方法深复制,集合里的每个对象都会收到 copyWithZone: 消息。同时集合里的对象需遵循 NSCopying 协议,否则会崩溃。


image.png

得到的结果如下:

image.png

从打印结果可以看出,拷贝后和拷贝前的数组中的DBTestModel对象的sourceArray的内存地址是相同的,这种拷贝方式只能够提供一层内存拷贝(one-level-deep copy),而非真正的深复制。

  • A true deep copy

使用归档来实现:

NSArray *copyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
       [NSKeyedArchiver archivedDataWithRootObject:array]];

小结

在项目中遇到需要需要对对象进行拷贝时,需要理解以下两点:
1、对系统自带的不可变对象进行copy时,为指针复制;
2、对集合类对象进行copymutableCopy时,集合类的元素均为指针复制;
3、如只需对集合进行单层深拷贝,可以使用initWithArray:copyItems:类似的方法,将第二个参数设为YES来实现,如需实现集合的完全复制,可以使用归解档来实现;
4、第三方库Mantle中的MTLModel类有实现NSCodingNSCopying协议,自定义的类继承MTLModel类即可实现NSCodingNSCopying协议。

参考链接

Object copying
Copying Collections

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