IOS开发中的深拷贝

前言

最近的开发中,遇到了一个诡异的有关深拷贝的bug,虽然在自己的努力的解决了问题,但我觉得有必要分享一下这个诡异的过程。。

过程描述

开发中我遇到一个需要临时拷贝数组,并对临时数组中数据进行处理,再与原数组合并的场景,结果发现对临时数组中的数据进行处理之后,原数组的数据也发生的变化,这让我不得不去深究一下深拷贝的原理

还原问题与解决

之前的开发中的数据结构过于复杂,所以我写了一个demo,用来描述问题所在。请先看这段代码:

    people *p1 = [[people alloc]init];
    p1.name = @"张三";
    p1.age = @"20";
    p1.male = @"男";

    people *p2 = [[people alloc]init];
    p2.name = @"李四";
    p2.age = @"29";
    p2.male = @"男";

    self.peopleArray = [NSMutableArray arrayWithObjects:p1,p2, nil];

    self.templeArray = [self.peopleArray mutableCopy];

这里我创建了一个people模型,属性有如下几个:

@interface people : NSObject

@property (nonatomic,copy) NSString *name;

@property(nonatomic,copy)NSString *age;

@property(nonatomic,copy)NSString *male;

@end

代码中的templeArray是由peopleArray通过mutableCopy方法复制而来的,我们可以打印一下两者地址

Deep Screen_1

这时候可以看出,两个可变数组的内存地址是不一样的,也就是说,这两个数组理应不相互干涉。

现在我对第一个数组中的第一个people模型进行数据修改,将性别改为“女”,理论上来说第二个数组中的数据应当不变,性别仍然为“男”。

people *templePeopel = self.peopleArray[0];
templePeopel.male = @"女";
people *p = self.templeArray[0];
NSLog(@"pa:%@,ta:%@",p.male,templePeopel.male);

这段代码根据我们的深拷贝要求打印出来应该是前者为女,后者为男,来看看结果。
Deep Screen_2

结果与我们预期的不一致,这里说明,当数组元素是自定义类的时候,不管深浅拷贝,数组里面的元素地址是不变的

为了解决这个问题,必须要在模型中做点事。
首先声明NSMutableCopying协议

#import <Foundation/Foundation.h>

@interface people : NSObject<NSMutableCopying>

@property (nonatomic,copy) NSString *name;

@property(nonatomic,copy)NSString *age;

@property(nonatomic,copy)NSString *male;

@end

再实现mutableCopyWithZone方法

-(id)mutableCopyWithZone:(NSZone *)zone{
    people *people = [[self class] allocWithZone:zone];
    people.name = [[self name] mutableCopy];
    people.age = [[self age] mutableCopy];
    people.male = [[self male] mutableCopy];
    return people;
}

最后写一个测试方法:

people *p1 = [[people alloc]init];
    p1.name = @"张三";
    p1.age = @"20";
    p1.male = @"男";

    people *p2 = [[people alloc]init];
    p2.name = @"李四";
    p2.age = @"29";
    p2.male = @"男";

    self.peopleArray = [NSMutableArray arrayWithObjects:p1,p2, nil];

    self.templeArray = [NSMutableArray array];
    [self.peopleArray enumerateObjectsUsingBlock:^(people *obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [self.templeArray addObject:[obj mutableCopy]];
    }];

    people *templePeopel = self.peopleArray[0];
    templePeopel.male = @"女";

    people *p = self.templeArray[0];

    NSLog(@"%p,%p",self.peopleArray,self.templeArray);
    NSLog(@"%p,%p",templePeopel,p);
    NSLog(@"pa:%@,ta:%@",p.male,templePeopel.male);

测试结果:

Deep_Screen

深复制成功。

小结

这次这个问题是在实际开发工作中遇到的,对于copy方法,我也做了测试,这里就不再赘述,有兴趣的朋友可以多写一点demo来玩玩。
对于copymutableCopy,有一张图可以很清楚的叙述其中的原理。

Deep_Image

在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;对mutable对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:

  • [immutableObject copy] // 浅复制
  • [immutableObject mutableCopy] //深复制
  • [mutableObject copy] //深复制
  • [mutableObject mutableCopy] //深复制
本文总阅读量