OC之数组的copy方法

浅拷贝和深拷贝

苹果官方文档是这样解释的

There are two kinds of object copying: shallow copies and deep copies. The normal copy is a shallow copy that produces a new collection that shares ownership of the objects with the original. Deep copies create new objects from the originals and add those to the new collection.

谷歌翻译

有两种对象复制:浅拷贝和深拷贝。 正常副本是一个浅拷贝,它产生一个新集合,它与原始对象共享对象的所有权。 深度副本从原始文件创建新对象,并将它们添加到新集合。

更直观的解释如下图:

CopyingCollections_2x.png

NSArray的copy

NSArray内部已经实现了NSCopying协议,所以可以直接调用copy方法,假如其内部没实现的话就需要我们自己实现- (id)copyWithZone:(NSZone *)zone方法,否则就会报错。NSMutableArray同理。
而Foundation框架中提供的所有集合默认都是浅拷贝,验证如下

新建Student和Name类,代码挺简单,就不做解释了

@interface Name : NSObject<NSCopying>

@property (nonatomic, copy) NSString *surname; // 姓
@property (nonatomic, copy) NSString *firstName; // 名

@end

@implementation Name

- (id)copyWithZone:(NSZone *)zone{
    Name *copy = [[[self class] allocWithZone:zone] init];
    return copy;
}

@end

@interface Student : NSObject<NSCopying>

@property (nonatomic, strong) Name *name;
@property (nonatomic, strong) NSString *address;

@end

@implementation Student

- (instancetype)init {
    self = [super init];
    if (self) {

    }
    return self;
}

- (id)copyWithZone:(NSZone *)zone {
    Student *copy = [[[self class] allocWithZone:zone] init];
    copy.name = [self.name copy];
    return copy;
}
@end

然后,构建NSArray,通过mutableCopy拷贝一份并打印拷贝前后两者的地址进行比较,代码如下

    Student *student = [Student new];
    Name *name = [Name new];
    student.name = name;

    NSMutableArray *studentsArray = [NSMutableArray new];
    [studentsArray addObject:student];
    NSMutableArray *studentsArrayCopy = [studentsArray mutableCopy];

    NSLog(@"\nstudentsArray[0]:%p",studentsArray[0]);
    NSLog(@"\nstudentsArrayCopy[0]:%p",studentsArrayCopy[0]);
    NSLog(@"\nstudentsArrayAddress:%p",studentsArray);
    NSLog(@"\nstudentsArrayCopyAddress:%p",studentsArrayCopy);

打印结果

studentsArray[0]:0x1002013b0
studentsArrayCopy[0]:0x1002013b0
studentsArrayAddress:0x100203210
studentsArrayCopyAddress:0x100203320

可见studentsArray(原数据)和studentsArrayCopy(拷贝后的数据)地址是不同的,而数组中的元素的地址相同,可见通过mutableCopy方法实现的拷贝是浅拷贝。

数组还提供了一种copy方法,- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;,flag表示是否把数组元素也拷贝一份,如果数组的元素没有实现NSCopying协议而且flag设置成YES的话,调用该方法是会Crash的。如果数组中的所有层级都实现了NSCopying方法的话,这种方式的拷贝就是所谓的深拷贝,即完全复制一份。验证如下

    Student *student = [Student new];
    Name *name = [Name new];
    student.name = name;
    
    
    NSMutableArray *studentsArray = [NSMutableArray new];
    [studentsArray addObject:student];
    NSMutableArray *studentsArrayCopy = [[NSMutableArray alloc] initWithArray:studentsArray copyItems:YES];

    NSLog(@"\nstudentsArray[0]:%p",studentsArray[0]);
    NSLog(@"\nstudentsArrayCopy[0]:%p",studentsArrayCopy[0]);
    NSLog(@"\nstudentsArrayAddress:%p",studentsArray);
    NSLog(@"\nstudentsArrayCopyAddress:%p",studentsArrayCopy);
    
    Name *nameCopy = ((Student *)studentsArrayCopy[0]).name;
    NSLog(@"\nname:%p",name);
    NSLog(@"\nnameCopy:%p",nameCopy);

打印结果如下

studentsArray[0]:0x100300070
studentsArrayCopy[0]:0x100300930
studentsArrayAddress:0x100300700
studentsArrayCopyAddress:0x100300a00
name:0x100300130
nameCopy:0x1003009e0

所有的原对象和copy对象都不同,可见是深拷贝。当我们要实现复制一个数组并在操作之后对原数组无影响的需求时就可以通过这种方式。
当然,如果数组层次很多的话,这方式明显不可取,幸运的是苹果提供了一种更简单的实现方式(本人没验证过)

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

还有,- (instancetype)initWithArray:(NSArray<ObjectType> *)array;这种拷贝方式和- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;方式是一样的,当后者的flag设置成NO的时候,这两个这两方法是完全相同的。而且这样方式和直接调用copy方式的拷贝也是一样的

    Student *student = [Student new];
    Name *name = [Name new];
    student.name = name;
    
    
    NSMutableArray *studentsArray = [NSMutableArray new];
    [studentsArray addObject:student];
    NSMutableArray *studentsArrayCopy = [[NSMutableArray alloc] initWithArray:studentsArray copyItems:NO];

    NSLog(@"\nstudentsArray[0]:%p",studentsArray[0]);
    NSLog(@"\nstudentsArrayCopy[0]:%p",studentsArrayCopy[0]);
    NSLog(@"\nstudentsArrayAddress:%p",studentsArray);
    NSLog(@"\nstudentsArrayCopyAddress:%p",studentsArrayCopy);
    
    Name *nameCopy = ((Student *)studentsArrayCopy[0]).name;
    NSLog(@"\nname:%p",name);
    NSLog(@"\nnameCopy:%p",nameCopy);

打印结果

studentsArray[0]:0x1004041d0
studentsArrayCopy[0]:0x1004041d0
studentsArrayAddress:0x1004060c0
studentsArrayCopyAddress:0x100406260
name:0x100405ef0
nameCopy:0x100405ef0

数组地址不同,内部元素相同。没毛病。

总结

  • 浅拷贝:
    1, 直接调用copy/mutableCopy方法
    2, [[NSMutableArray alloc] initWithArray:studentsArray copyItems:NO]
    3, [[NSMutableArray alloc] initWithArray:studentsArray]
  • 深拷贝:
    1, [[NSMutableArray alloc] initWithArray:studentsArray copyItems:YES],前提要自己实现每一层级的NSCopying协议
    2, [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]]

参考链接:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Collections/Articles/Copying.html

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

推荐阅读更多精彩内容