天天看点

iOS 深拷贝和浅拷贝深拷贝和浅拷贝三、 模型Model的数组拷贝

深拷贝和浅拷贝

#需求:

A模型 中存放着 多个自定义模型TagModel,

需要将 A模型 中的TagModel数组 拷贝到 B数组 中,

并修改 B数组中的元素,不能影响到A数组,

如果B数组保存成功后回调替换 A模型中的tagModel数组。

这是项目中的常见场景,也是数组拷贝的常见问题,这里分几部讲解

1.深拷贝和浅拷贝的概念;

2.简单数组元素的拷贝;

3.模型数组元素的拷贝;

#一、 深拷贝和浅拷贝的概念

浅拷贝即指针拷贝,不会产生新的对象,使用的是同一块内存地址。浅拷贝是对内存地址的复制,让目标对象指向 和 源对象 指向同一片内存空间。

深拷贝是内容拷贝,会产生新的对象,产生新的地址。

iOS 深拷贝和浅拷贝深拷贝和浅拷贝三、 模型Model的数组拷贝
iOS 深拷贝和浅拷贝深拷贝和浅拷贝三、 模型Model的数组拷贝

#二、 基本类型 的数组拷贝

###2.1 NSArray 与 copy/mutableCopy

运行代码:

NSArray  *normalArray = [NSMutableArray arrayWithObjects:@(0),@(1),@(2), nil];
    id copyArray = [normalArray copy];
    id mutableCopyArray = [normalArray mutableCopy];
    NSLog(@"tempArray=%@",[copyArray isKindOfClass:[NSMutableArray class]] [email protected]"可变数组":@"不可变数组");
    NSLog(@"tempArray2=%@",[mutableCopyArray isKindOfClass:[NSMutableArray class]] [email protected]"可变数组":@"不可变数组");
    mutableCopyArray[0] = @(80);
    NSLog(@" \nnormalArray=%p\n copyArray=%p\n mutableCopyArray=%p",normalArray,copyArray,mutableCopyArray);
    NSLog(@"normalArray=%@\n copyArray=%@\n mutableCopyArray=%@",normalArray,copyArray,mutableCopyArray);
    
           

打印结果为:

2019-09-03 22:34:18.932275+0800 xiaojian[901:136568] tempArray=不可变数组
2019-09-03 22:34:18.932467+0800 xiaojian[901:136568] tempArray2=可变数组
2019-09-03 22:34:18.932535+0800 xiaojian[901:136568]  
    normalArray =0x28333c0c0
    copyArray =0x28333e2e0
    mutableCopyArray =0x28333e250
2019-09-03 22:34:18.933320+0800 xiaojian[901:136568] normalArray=(
    0,
    1,
    2
)
 copyArray=(
    0,
    1,
    2
)
 mutableCopyArray=(
    80,
    1,
    2
)
           

normalArray 和 copyArray 为同一块内存地址,mutableCopyArray为新的内存地址。但是数组中指针还是同一块内存地址。

断点打印结果:

(lldb) p normalArray[0]
(__NSCFNumber *) $2 = 0xe7f5604f3336fa41 (int)0
(lldb) p copyArray[0]
(__NSCFNumber *) $3 = 0xe7f5604f3336fa41 (int)0
(lldb) p mutableCopyArray[0]
(__NSCFNumber *) $4 = 0xe7f5604f3336ff41 (int)80
(lldb) p normalArray[1]
(__NSCFNumber *) $5 = 0xe7f5604f3336fa51 (int)1
(lldb) p copyArray[1]
(__NSCFNumber *) $6 = 0xe7f5604f3336fa51 (int)1
(lldb) p mutableCopyArray[1]
(__NSCFNumber *) $7 = 0xe7f5604f3336fa51 (int)1
           

注意:

mutablecopy后 虽然数组的内存地址变了,但是数组中存放的地址指针没有改变。

mutableCopyArray[0] 的 值和内存地址 发生改变,是因为直接改变了 mutableCopyArray 中 数组元素中的的 内存地址指针,并没有改变 旧内存地址指针指向的值。

#####结论:

对 不可变数组 进行 copy 不会开辟新的内存空间,生成的是不可变对象,指向的是同一块内存地址。

对 不可变数组 进行 mutableCopy 会开辟新的内存空间,生成的是可变对象,两个数组中的内容还是同一块内存地址。 另外赋值,只是改变了内存地址,没有改变旧地址中的值。

###2.2 NSMutableArray 与 copy/mutableCopy

运行代码:

NSMutableArray *normalArray = [NSMutableArray arrayWithObjects:@(0),@(1),@(2), nil];
    id copyArray = [normalArray copy];
    id mutableCopyArray = [normalArray mutableCopy];
    NSLog(@"tempArray=%@",[copyArray isKindOfClass:[NSMutableArray class]] [email protected]"可变数组":@"不可变数组");
    NSLog(@"tempArray2=%@",[mutableCopyArray isKindOfClass:[NSMutableArray class]] [email protected]"可变数组":@"不可变数组");
    mutableCopyArray[0] = @(80);
    NSLog(@"\nnormalArray=%p\ncopyArray=%p\nmutableCopyArray=%p",normalArray,copyArray,mutableCopyArray);
    NSLog(@"normalArray=%@\n copyArray=%@\n mutableCopyArray=%@",normalArray,copyArray,mutableCopyArray);
           

打印结果:

2019-09-03 19:01:48.513104+0800 xiaojian[780:105418] tempArray=不可变数组
2019-09-03 19:01:48.513287+0800 xiaojian[780:105418] tempArray2=可变数组
2019-09-03 19:01:48.513352+0800 xiaojian[780:105418]  
 normalArray        =0x280fe1350
 copyArray          =0x280fe2220
 mutableCopyArray   =0x280fe1320
2019-09-03 19:01:48.514100+0800 xiaojian[780:105418] normalArray=(
    0,
    1,
    2
)
 copyArray=(
    0,
    1,
    2
)
 mutableCopyArray=(
    80,
    1,
    2
)
           

断点调节结果:

(lldb)  p normalArray[0]
(__NSCFNumber *) $2 = 0xf20f2182ce45ec56 (int)0
(lldb)  p copyArray[0]
(__NSCFNumber *) $3 = 0xf20f2182ce45ec56 (int)0
(lldb)  p mutableCopyArray[0]
(__NSCFNumber *) $4 = 0xf20f2182ce45e956 (int)80
(lldb)  p normalArray[1]
(__NSCFNumber *) $5 = 0xf20f2182ce45ec46 (int)1
(lldb)  p copyArray[1]
(__NSCFNumber *) $6 = 0xf20f2182ce45ec46 (int)1
(lldb)  p mutableCopyArray[1]
(__NSCFNumber *) $7 = 0xf20f2182ce45ec46 (int)1
           

####结论:

可变数组进行 copy ,会开辟新的内存地址,生成一个不可变数组。数组中的内存地址跟拷贝的数组 还是一样的。

可变数组进行mutableCopy, 会开辟新的内存地址,生成一个可变数组。数组中的内存地址跟 拷贝的数组 还是一样的。

三、 模型Model的数组拷贝

数组仍然遵循上述规则,***但是模型是不拷贝的***。

运行代码:

//初始化
    UserModel *user = [[UserModel alloc] init];
    user.name = @"张三";
    user.age = 20;
    NSMutableArray *normalArray = [[NSMutableArray alloc] initWithObjects:user, nil];
    //数组拷贝
    id mutableArray = [normalArray mutableCopy];
    NSLog(@"mutableArray 是 %@",[mutableArray isKindOfClass:[NSMutableArray class]][email protected]"可变数组":@"不可变数组");
    //打印信息
    NSLog(@"normalArray 内存地址为=%p,内容为 %@",normalArray,normalArray);
    NSLog(@"mutableArray内存地址为=%p,内容为 %@",mutableArray,mutableArray);
           

打印结果为

2019-09-03 23:03:30.805146+0800 xiaojian[920:141077] mutableArray 是 可变数组
2019-09-03 23:03:30.805481+0800 xiaojian[920:141077] normalArray 内存地址为=0x283a06100,内容为 (
    "<UserModel: 0x2833c7ba0>"
)
2019-09-03 23:03:30.805611+0800 xiaojian[920:141077] mutableArray内存地址为=0x283a063d0,内容为 (
    "<UserModel: 0x2833c7ba0>"
)

           

总结:数组拷贝,仍遵循上面的原则,但是数组中的模型是同一个对象,进行了指针拷贝,即没有对对象进行深拷贝。

#四、 Model深度拷贝最终解决办法

####4.1 initWithArray:copyItems:

苹果的官方文档:

https://developer.apple.com/documentation/foundation/nsarray/1408557-initwitharray

解释:

如果copyItems 后面的参数:

flag=YES,则新数组中的对象都接收 copyWithZone: 方法,来创建对象的副本,但是对象必须遵循NSCopying 协议。

flag=NO,则新数组中的每个对象,都是使用的retain,即跟旧数组中的对象 为 同一内存地址。

flag为YES必须遵循copyWithZone。否则会报错。

2019-09-04 10:14:20.720035+0800 xiaojian[3204:559327] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UserModel copyWithZone:]: unrecognized selector sent to instance 0x282ae1320'
           
UserModel *user = [[UserModel alloc] init];
    user.name = @"张三";
    user.age = 20;
    NSMutableArray *normalArray = [[NSMutableArray alloc] initWithObjects:user, nil];
    //数组拷贝
    id mutableArray = [[NSMutableArray alloc] initWithArray:normalArray copyItems:YES];;
    NSLog(@"mutableArray 是 %@",[mutableArray isKindOfClass:[NSMutableArray class]][email protected]"可变数组":@"不可变数组");
    //打印信息
    NSLog(@"normalArray 内存地址为=%p,内容为 %@",normalArray,normalArray);
    NSLog(@"mutableArray内存地址为=%p,内容为 %@",mutableArray,mutableArray);
           

Model层的拷贝如下

///.h文件
#import <Foundation/Foundation.h>
@interface UserModel : NSObject<NSCopying>

@property (nonatomic ,copy) NSString *userID;
@property (nonatomic ,copy) NSString *name;
@property (nonatomic ,assign) NSInteger age;

@end

///.m文件
#import "UserModel.h"
@implementation UserModel
- (id)copyWithZone:(NSZone *)zone {
    UserModel * model = [[UserModel allocWithZone:zone] init];
    model.name      = self.name;
    model.userID    = self.userID;
    model.age       = self.age;
    return model;
}
@end
           

打印结果

2019-09-16 14:19:46.364620+0800 xiaojian[468:35561] mutableArray 是 可变数组
2019-09-16 14:19:46.364920+0800 xiaojian[468:35561] normalArray 内存地址为=0x280021890,内容为 (
    "<UserModel: 0x2809f4280>"
)
2019-09-16 14:19:46.365037+0800 xiaojian[468:35561] mutableArray内存地址为=0x280022a60,内容为 (
    "<UserModel: 0x2809f4780>"
)
           

生成新的UserModel模型,开辟新的存储空间,原来的UserModel基本元素相互不影响。

注意:模型中嵌套模型,也可以进行拷贝,开辟新的内存空间。

但是:主模型数组中的模型不会开辟新的内存空间,仍然是同一个对象。

4.2 归档和解档

苹果官网文档:

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Collections/Articles/Copying.html#//apple_ref/doc/uid/TP40010162-SW1

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