复制简介
- 关于深复制与浅复制的简介:iOS 深复制与浅复制
- 实现完全深复制的方法有两种:协议方法和归档解档
- 归档和解档的概念补充:
有时存在这样的需求,即将程序中使用的多个对象及其属性值,以及它们的相互关系保存到文件中,或者发送给另外的进程。为了实现此功能,foundation框架中,可以把相互关联的多个对象归档为二进制文件,而且还能将对象的关系从二进制文件中还原出来。
归档:将对象打包成二进制文件。NSKeyedArchiver:归档器
解档:归档的逆变换。NSKeyedUnarchiver:解档器
因此可以利用归档和解档来实现完全复制
深复制
深复制(即单层深复制)改变新对象指针指向,还为对象生成新的内存地址。单层深复制虽然生成了新的内存地址,但是新的对象中的元素地址还是旧对象元素的地址,代码示例:
//对于可变对象执行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);
打印结果:
说明归档解档是可以实现嵌套数组的完全深复制的!