iOS 完全深复制

复制简介

  • 关于深复制与浅复制的简介:iOS 深复制与浅复制
  • 实现完全深复制的方法有两种:协议方法和归档解档
  • 归档和解档的概念补充:
    有时存在这样的需求,即将程序中使用的多个对象及其属性值,以及它们的相互关系保存到文件中,或者发送给另外的进程。为了实现此功能,foundation框架中,可以把相互关联的多个对象归档为二进制文件,而且还能将对象的关系从二进制文件中还原出来。
    归档:将对象打包成二进制文件。NSKeyedArchiver:归档器
    解档:归档的逆变换。NSKeyedUnarchiver:解档器
    因此可以利用归档和解档来实现完全复制

GitHub链接

深复制

深复制(即单层深复制)改变新对象指针指向,还为对象生成新的内存地址。单层深复制虽然生成了新的内存地址,但是新的对象中的元素地址还是旧对象元素的地址,代码示例:

//对于可变对象执行mutableCopy为深拷贝
    NSMutableString *mutableString = [NSMutableString stringWithFormat:@"%d", 123];
    NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:mutableString, @"bbb", nil];
    NSArray *copyMutableArray = [mutableArray mutableCopy];
    [mutableString appendString:@"111"];
    NSLog(@"%p   ----    %p", mutableArray, copyMutableArray);
    NSLog(@"%@   ----    %@", mutableArray, copyMutableArray);

打印结果:

0x600003240750   ----    0x6000032403f0
(
    123111,
    bbb
)   ----    (
    123111,
    bbb
)

得知:当我们在对数组进行copy时,我们只是建立了一个指向相同地址的指针,改变地址指向的值,copy生成的数组也会被改变,我们就需要用到完全深复制了

完全深复制

协议方法

使用 copyItems:

集合对象使用这个方法,必须遵守协议,并重写- (id)copyWithZone:(NSZone *)zone方法。

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ModelStudent : NSObject<NSCopying>

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

@end

NS_ASSUME_NONNULL_END

在.m文件中重写方法

- (id)copyWithZone:(NSZone *)zone {
    ModelStudent *student = [[ModelStudent alloc] init];
    student.name = self.name;
    student.age = self.age;
    student.sex = self.sex;
    return student;
}

执行copy

	ModelStudent *student1 = [[ModelStudent alloc] init];
    student1.name = @"111";
    student1.age = 10;
    student1.sex = 1;
    
    ModelStudent *student2 = [[ModelStudent alloc] init];
    student2.name = @"222";
    student2.age = 20;
    student2.sex = 0;
    
    self.array = @[student1, student2];
    NSArray *copyArray = [[NSArray alloc] initWithArray:_array copyItems:YES];
    NSLog(@"array %@ \ncopy array %@", _array, copyArray);

打印结果:

array (
    "<ModelStudent: 0x600000fb1220>",
    "<ModelStudent: 0x600000fb1260>"
) 
copy array (
    "<ModelStudent: 0x600000fb11e0>",
    "<ModelStudent: 0x600000fb12a0>"
)

注意:如果我放入的是未实现NSCopying协议的对象,如自定义对象,调用这个方法甚至会crash,因为自定义对象没有copy方法。
copyArray里元素地址与array中元素地址不同,说明已经实现了完全深复制,但该协议方法只能应用于单层数组,对于多层数组,上面的方法就失效了,例如:

	ModelStudent *student1 = [[ModelStudent alloc] init];
    student1.name = @"111";
    student1.age = 10;
    student1.sex = 1;
    
    ModelStudent *student2 = [[ModelStudent alloc] init];
    student2.name = @"222";
    student2.age = 20;
    student2.sex = 0;
    
    self.array = @[student1, student2];
    NSArray *manyArray = [[NSArray alloc] initWithObjects:_array, @"123", nil];
    NSArray *copyArray = [[NSArray alloc] initWithArray:manyArray copyItems:YES];
    NSLog(@"manyArray %@ \ncopy array %@", manyArray, copyArray);

打印结果:

manyArray (
        (
        "<ModelStudent: 0x600000653e60>",
        "<ModelStudent: 0x600000653f40>"
    ),
    123
) 
copy array (
        (
        "<ModelStudent: 0x600000653e60>",
        "<ModelStudent: 0x600000653f40>"
    ),
    123
)

这是因为 NSArray *copyArray = [[NSArray alloc] initWithArray:manyArray copyItems:YES]; 只能进行一次完全深复制,遇见多层数组时就失效了。

这时我们就可以自己实现深拷贝协议

自己实现深拷贝协议

方法来源于iOS 深拷贝两种实现
使用Category分类添加深拷贝方法
这里以NSArray为例:

@interface NSArray (DeepCopy)

- (instancetype)deepCopy;

@end
```objectivec

```objectivec
#import "NSArray+DeepCopy.h"

@implementation NSArray (DeepCopy)

- (instancetype)deepCopy {
    NSMutableArray *mutableResultArray = [[NSMutableArray alloc] initWithCapacity:[self count]];
    for (id subObject in self) {
        id deepCopySubObject = nil;
        if ([subObject respondsToSelector:@selector(deepCopy)]) {   // 比如NSArray嵌套
            deepCopySubObject = [subObject deepCopy];
        } else if ([subObject isKindOfClass:[NSMutableArray class]] || [subObject isKindOfClass:[NSMutableSet class]] || [subObject isKindOfClass:[NSMutableDictionary class]]) {
            deepCopySubObject = [subObject mutableCopy];   // 对于单层mutable元素mutableCopy就是深复制
        } else if ([subObject conformsToProtocol:@protocol(NSCopying)]) {
            deepCopySubObject = [subObject copy];  // 如果实现了NSCopying协议 就直接copy
        } else {
            deepCopySubObject = subObject;
        }

        if (deepCopySubObject) {
            [mutableResultArray addObject:deepCopySubObject];
        } else {
            [mutableResultArray addObject:subObject];
        }
    }

    if ([self isKindOfClass:[NSMutableArray class]]) {
        return mutableResultArray;
    } else {
        return [NSArray arrayWithArray:mutableResultArray];
    }
}

@end

测试:

    ModelStudent *student1 = [[ModelStudent alloc] init];
    student1.name = @"111";
    student1.age = 10;
    student1.sex = 1;
    
    ModelStudent *student2 = [[ModelStudent alloc] init];
    student2.name = @"222";
    student2.age = 20;
    student2.sex = 0;
        
    self.array = @[student1, student2];
    NSMutableString *string = [[NSMutableString alloc] initWithString:@"123"];
    NSArray *manyArray = [[NSArray alloc] initWithObjects:_array, string, nil];
    NSArray *copyArray = [manyArray deepCopy];
    [string appendString:@"123"];
    NSLog(@"manyArray %@ \ncopy array %@", manyArray, copyArray);

在这里插入图片描述

归档解档

使用NSKeyedArchiver压缩对象成二进制数据,再使用NSKeyedUnarchiver解压二进制数据。通常我们对模型数组完全复制,先将模型数组转换为data数组,再将data数组转换为模型数组,即可
归档:

	ModelStudent *student1 = [[ModelStudent alloc] init];
    student1.name = @"111";
    student1.age = 10;
    student1.sex = 1;
    
    ModelStudent *student2 = [[ModelStudent alloc] init];
    student1.name = @"111222";
    student1.age = 120;
    student1.sex = 12;
    
    self.array = @[student1, student2];
    
     //沙盒ducument目录
     NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
     //完整的文件路径
     NSString *path = [docPath stringByAppendingPathComponent:@"student.archiver"];
     
     //将数据归档到path文件路径里面
     NSData *data = [NSKeyedArchiver archivedDataWithRootObject:_array requiringSecureCoding:NO error:nil];
     BOOL success = [data writeToFile:path atomically:YES];
     
     if (success) {
         NSLog(@"归档成功");
     }else {
         NSLog(@"归档失败");
     }

解档:

    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *path = [docPath stringByAppendingPathComponent:@"student.archiver"];
    NSArray *copyArray = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    NSLog(@"%@\n%@", _array, copyArray);

打印结果:
在这里插入图片描述

多层数组时:
归档:

     ModelStudent *student1 = [[ModelStudent alloc] init];
     student1.name = @"111";
     student1.age = 10;
     student1.sex = 1;
    
    ModelStudent *student2 = [[ModelStudent alloc] init];
    student1.name = @"111222";
    student1.age = 120;
    student1.sex = 12;
    
    NSMutableString *string = [[NSMutableString alloc] initWithString:@"123"];
    NSArray *aArray = @[@"123", string];
    self.array = @[aArray, student1, student2];
    
     //沙盒ducument目录
     NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
     //完整的文件路径
     NSString *path = [docPath stringByAppendingPathComponent:@"student.archiver"];
     
     //将数据归档到path文件路径里面
     NSData *data = [NSKeyedArchiver archivedDataWithRootObject:_array requiringSecureCoding:NO error:nil];
     BOOL success = [data writeToFile:path atomically:YES];
     
     if (success) {
         NSLog(@"归档成功");
     }else {
         NSLog(@"归档失败");
     }
    
    [string appendString:@"222"];

解档:

    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *path = [docPath stringByAppendingPathComponent:@"student.archiver"];
    NSArray *copyArray = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    NSLog(@"%@\n%@", _array, copyArray);

打印结果:
在这里插入图片描述
说明归档解档是可以实现嵌套数组的完全深复制的!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值