持续创作,加快生长!这是我参加「日新方案 10 月更文挑战」的第6天,点击查看活动详情

深仿制与浅仿制是在内存办理中非常重要的概念,了解好深仿制和浅仿制也有助于加深对iOS的内存办理的了解。

深仿制与浅仿制的概念

浅仿制便是内存地址的仿制,仿制指向原来目标的指针,使原目标引用计数+1。 能够了解为创建了一个指向原目标的新指针,并没有创建一个全新的目标。

iOS之深拷贝与浅拷贝

深仿制便是指仿制目标的具体内容,仿制出来后两个目标虽然存的值是相同的,可是内存地址不一样,两个目标也互不影响。

iOS之深拷贝与浅拷贝

那么怎样才干判别是深仿制还是浅仿制呢,有一个很简单的办法便是经过打印目标的内存地址来判别是否是同一个目标,假如内存地址不同则代表是新拓荒的内存空间,为深仿制;假如内存地址相同则代表没有拓荒新的内存空间,为浅仿制。

下面就以数组为例来验证一下不同状况的深仿制与浅仿制:

装有根本类型元素的数组仿制

NSArray与Copy

NSString *str1 = @"1";
NSString *str2 = @"2";
NSArray *array1 = [[NSArray alloc] initWithObjects:str1, str2, nil];
id array2 = [array1 copy];
[array2 isKindOfClass:[NSMutableArray class]] ? NSLog(@"array2为可变数组") : NSLog(@"array2为不行变数组");
NSLog(@"array1 address:%p, array2 address:%p", array1, array2);

打印成果如下:

2021-01-12 09:35:57.753827+0800 ShallowCopy&DeepCopy[6036:27261356] array2为不行变数组
2021-01-12 09:35:57.753966+0800 ShallowCopy&DeepCopy[6036:27261356] array1 address:0x600002734be0, array2 address:0x600002734be0

定论:不行变数组进行copy,不会拓荒新的内存空间也不会生成不行变目标,array1与array2指向同一个内存地址。

NSMutableArray与copy

NSMutableArray *array1 = [[NSMutableArray alloc] initWithObjects:@"1", @"2", nil];
id array2 = [array1 copy];
[array2 isKindOfClass:[NSMutableArray class]] ? NSLog(@"array2为可变数组") : NSLog(@"array2为不行变数组");
array1[0] = @"3";
NSLog(@"array1 address:%p, array2 address:%p", array1, array2);
NSLog(@"array1:%@, array2:%@", array1, array2);

打印成果如下:

2021-01-12 09:44:34.714372+0800 ShallowCopy&DeepCopy[9999:27285362] array2为不行变数组
2021-01-12 09:44:34.714558+0800 ShallowCopy&DeepCopy[9999:27285362] array1 address:0x600000657cc0, array2 address:0x600000805320
2021-01-12 09:44:34.714781+0800 ShallowCopy&DeepCopy[9999:27285362] array1:(
    3,
    2
), array2:(
    1,
    2
)

定论:对可变数组进行copy,会拓荒新的内存空间,生成一个新的不行变数组,两个数组互不影响。

NSArray与mutableCopy

NSArray *array1 = [[NSArray alloc] initWithObjects:@"1", @"2", nil];
id array2 = [array1 mutableCopy];
[array2 isKindOfClass:[NSMutableArray class]] ? NSLog(@"array2为可变数组") : NSLog(@"array2为不行变数组");
array2[0] = @"3";
NSLog(@"array1 address:%p, array2 address:%p", array1, array2);
NSLog(@"array1:%@, array2:%@", array1, array2);

打印成果为:

2021-01-12 09:48:30.798739+0800 ShallowCopy&DeepCopy[11740:27295204] array2为可变数组
2021-01-12 09:48:30.798892+0800 ShallowCopy&DeepCopy[11740:27295204] array1 address:0x600000344e60, array2 address:0x600000d34c90
2021-01-12 09:48:30.799102+0800 ShallowCopy&DeepCopy[11740:27295204] array1:(
    1,
    2
), array2:(
    3,
    2
)

定论:对不行变数组进行mutableCopy,会拓荒新的内存空间,生成一个可变数组、两个数组互不影响。

NSMutableArray与mutableCopy

NSMutableArray *array1 = [[NSMutableArray alloc] initWithObjects:@"1", @"2", nil];
id array2 = [array1 mutableCopy];
[array2 isKindOfClass:[NSMutableArray class]] ? NSLog(@"array2为可变数组") : NSLog(@"array2为不行变数组");
array1[0] = @"3";
NSLog(@"array1 address:%p, array2 address:%p", array1, array2);
NSLog(@"array1:%@, array2:%@", array1, array2);

打印成果为:

2021-01-12 09:52:21.895913+0800 ShallowCopy&DeepCopy[13524:27305490] array2为可变数组
2021-01-12 09:52:21.896071+0800 ShallowCopy&DeepCopy[13524:27305490] array1 address:0x600002a6d7d0, array2 address:0x600002a6db90
2021-01-12 09:52:21.896230+0800 ShallowCopy&DeepCopy[13524:27305490] array1:(
    3,
    2
), array2:(
    1,
    2
)

定论:可变数组进行mutableCopy,会拓荒新的内存空间、生成一个可变数组,两个数组互不影响。

下面便是总结好的copy和mutableCopy的成果

iOS之深拷贝与浅拷贝

装有可变字符串的数组仿制

装有根本类型的数组仿制咱们现已尝试过了,那么将字符串换成可变字符串之后还会遵从上述规矩么?

NSMutableString * str1 = [[NSMutableString alloc] initWithString:@"1"];
NSMutableString * str2 = [[NSMutableString alloc] initWithString:@"2"];
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:str1, str2, nil];
id array2 = [array1 mutableCopy];
[array2 isKindOfClass:[NSMutableArray class]] ? NSLog(@"array2为可变数组") : NSLog(@"array2为不行变数组");
[str1 appendString:@"3"];
NSLog(@"array1 address:%p, array2 address:%p", array1, array2);
NSLog(@"array1:%@, array2:%@", array1, array2);

打印成果为:

2021-01-12 10:18:36.709537+0800 ShallowCopy&DeepCopy[25809:27375890] array2为可变数组
2021-01-12 10:18:36.709683+0800 ShallowCopy&DeepCopy[25809:27375890] array1 address:0x600002514e10, array2 address:0x600002514d80
2021-01-12 10:18:36.709912+0800 ShallowCopy&DeepCopy[25809:27375890] array1:(
    13,
    2
), array2:(
    13,
    2
)

能够看到array2为可变数组,array1和array2的内存地址不同,证明了数组的确进行了深仿制,可是对str1进行append操作却影响到了这两个数组。这是由于什么?咱们来打印一下array1和array2内部数据的内存地址。

for (NSMutableString *str in array1) {
  NSLog(@"%p",str);
}
NSLog(@"==========");
for (NSMutableString *str in array2) {
  NSLog(@"%p",str);
}

打印成果如下:

2021-01-12 10:21:32.943522+0800 ShallowCopy&DeepCopy[27325:27385477] 0x6000003c0c60
2021-01-12 10:21:32.943650+0800 ShallowCopy&DeepCopy[27325:27385477] 0x6000003c0ba0
2021-01-12 10:21:32.943799+0800 ShallowCopy&DeepCopy[27325:27385477] ==========
2021-01-12 10:21:32.943926+0800 ShallowCopy&DeepCopy[27325:27385477] 0x6000003c0c60
2021-01-12 10:21:32.944042+0800 ShallowCopy&DeepCopy[27325:27385477] 0x6000003c0ba0

成果发现两个数组内部存储的可变字符串的内存地址相同。数组自身进行了深仿制但可变字符串仅仅进行了浅仿制。

装有模型元素的数组仿制

装有根本类型的数组仿制咱们现已尝试过了,那么装有模型元素的数组还会遵从上述规矩么,咱们来看一下

装有模型的可变数组的mutableCopy

Animal *animal1 = [[Animal alloc] init];
animal1.age = @"1";
animal1.name = @"animal1";
animal1.sex = 1;
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:animal1, nil];
id array2 = [array1 mutableCopy];
[array2 isKindOfClass:[NSMutableArray class]] ? NSLog(@"array2为可变数组") : NSLog(@"array2为不行变数组");
NSLog(@"array1 address:%p, array2 address:%p", array1, array2);
NSLog(@"array1:%@, array2:%@", array1, array2);

打印成果如下:

2021-01-12 14:15:59.713219+0800 ShallowCopy&DeepCopy[49942:28050025] array2为可变数组
2021-01-12 14:15:59.713366+0800 ShallowCopy&DeepCopy[49942:28050025] array1 address:0x600003d49ce0, array2 address:0x600003d49c80
2021-01-12 14:15:59.713559+0800 ShallowCopy&DeepCopy[49942:28050025] array1:(
    "<Animal: 0x600003d49d40>"
), array2:(
    "<Animal: 0x600003d49d40>"
)

成果发现与上面的可变字符串的成果相同:数组自身进行了深仿制,但内部的目标只进行了浅仿制。

单层仿制

关于上面的两种状况来说,数组的深仿制并非严格意义上的深仿制,只能算是单层深仿制,虽然数组新拓荒了内存空间,可是数组里存放的元素仍然是之前数组元素的值,并没有别的仿制一份。

那么如何才干真实的完成深仿制呢?

办法一:遍历数组,对每个元素深仿制

Animal *animal1 = [[Animal alloc] init];
animal1.age = @"1";
animal1.name = @"animal1";
animal1.sex = 1;
Animal *animal2 = [[Animal alloc] init];
animal2.age = @"2";
animal2.name = @"animal2";
animal2.sex = 2;
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:animal1, animal2, nil];
NSMutableArray *array2 = [NSMutableArray arrayWithCapacity:0];
for (Animal *animal in array1) {
  [array2 addObject:[animal mutableCopy]];
}
NSLog(@"array1 address:%p, array2 address:%p", array1, array2);
NSLog(@"array1:%@, array2:%@", array1, array2);

打印成果如下:

2021-01-12 14:34:53.842362+0800 ShallowCopy&DeepCopy[58333:28097043] array1 address:0x600002f3a790, array2 address:0x600002f3a250
2021-01-12 14:34:53.842640+0800 ShallowCopy&DeepCopy[58333:28097043] array1:(
    "<Animal: 0x600002f3a8e0>",
    "<Animal: 0x600002f3a850>"
), array2:(
    "<Animal: 0x600002f3a670>",
    "<Animal: 0x600002f3a190>"
)

从打印成果能够看出,array2进行了深仿制,array2内部的元素也进行了深仿制。

办法二:initWithArray:copyItems

可是这种办法需要对元素进行遍历,不是很友好。其实官方现已供给了别的的一种办法initWithArray:copyItems

Animal *animal1 = [[Animal alloc] init];
animal1.age = @"1";
animal1.name = @"animal1";
animal1.sex = 1;
Animal *animal2 = [[Animal alloc] init];
animal2.age = @"2";
animal2.name = @"animal2";
animal2.sex = 2;
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:animal1, animal2, nil];
id array2 = [[NSMutableArray alloc] initWithArray:array1 copyItems:YES];
NSLog(@"array1 address:%p, array2 address:%p", array1, array2);
NSLog(@"array1:%@, array2:%@", array1, array2);

打印成果如下:

2021-01-12 14:41:17.060980+0800 ShallowCopy&DeepCopy[61190:28113308] array1 address:0x600000aa0090, array2 address:0x600000aa1020
2021-01-12 14:41:17.061229+0800 ShallowCopy&DeepCopy[61190:28113308] array1:(
    "<Animal: 0x600000aa0c30>",
    "<Animal: 0x600000aa00c0>"
), array2:(
    "<Animal: 0x600000aa0c90>",
    "<Animal: 0x600000aa0030>"
)

从打印成果来看和咱们遍历得到的成果是一致的。

咱们在这个基础上再添加一层模型目标试一下

Animal *animal1 = [[Animal alloc] init];
animal1.age = @"1";
animal1.name = @"animal1";
animal1.sex = 1;
animal1.dog = [[Dog alloc] init];
animal1.dog.age = @"1";
animal1.dog.name = @"dog1";
animal1.dog.sex = 1;
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:animal1, nil];
id array2 = [[NSMutableArray alloc] initWithArray:array1 copyItems:YES];
NSLog(@"array1 address:%p, array2 address:%p", array1, array2);
for (Animal *animal in array1) {
  NSLog(@"animal:%p. dog:%p", animal, animal.dog);
}
NSLog(@"==========");
for (Animal *animal in array2) {
  NSLog(@"animal:%p. dog:%p", animal, animal.dog);
}

打印成果如下:

2021-01-12 14:57:59.161465+0800 ShallowCopy&DeepCopy[68732:28155896] array1 address:0x6000003e1fe0, array2 address:0x6000003e1c80
2021-01-12 14:57:59.161668+0800 ShallowCopy&DeepCopy[68732:28155896] animal:0x6000003e1f20. dog:0x600000dba960
2021-01-12 14:57:59.161805+0800 ShallowCopy&DeepCopy[68732:28155896] ==========
2021-01-12 14:57:59.161925+0800 ShallowCopy&DeepCopy[68732:28155896] animal:0x6000003e2010. dog:0x600000dba980

咱们在这个基础上再添加一层数组试一下

Animal *animal1 = [[Animal alloc] init];
animal1.age = @"1";
animal1.name = @"animal1";
animal1.sex = 1;
Dog *dog1 = [[Dog alloc] init];
dog1.age = @"1";
dog1.name = @"dog1";
dog1.sex = 1;
animal1.modelArray = [[NSMutableArray alloc] init];
[animal1.modelArray addObject:dog1];
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:animal1, nil];
id array2 = [[NSMutableArray alloc] initWithArray:array1 copyItems:YES];
NSLog(@"array1 address:%p, array2 address:%p", array1, array2);
for (Animal *animal in array1) {
  NSLog(@"animal:%p. dog:%p", animal, animal.modelArray);
}
NSLog(@"==========");
for (Animal *animal in array2) {
  NSLog(@"animal:%p. dog:%p", animal, animal.modelArray);
}

打印成果如下:

2021-01-12 14:54:08.419263+0800 ShallowCopy&DeepCopy[66975:28145985] array1 address:0x600001f8a0a0, array2 address:0x600001f89650
2021-01-12 14:54:08.419429+0800 ShallowCopy&DeepCopy[66975:28145985] animal:0x600001f89da0. dog:0x600001f89fb0
2021-01-12 14:54:08.419263+0800 ShallowCopy&DeepCopy[66975:28145985] array1 address:0x600001f8a0a0, array2 address:0x600001f89650
2021-01-12 14:54:08.419429+0800 ShallowCopy&DeepCopy[66975:28145985] animal:0x600001f89da0. dog:0x600001f89fb0
2021-01-12 14:54:08.419553+0800 ShallowCopy&DeepCopy[66975:28145985] ==========
2021-01-12 14:54:08.419674+0800 ShallowCopy&DeepCopy[66975:28145985] animal:0x600001f89a10. dog:0x6000013f4cb0

经过成果能够发现模型中的数组不会拓荒新的内存空间,仍然是同一个目标。

所以运用initWithArray:copyItems时模型自身的属性都会进行深仿制,但假如模型属性还包括数组,那这个办法就不管用了。

办法三:归档解档

还有别的一种办法,便是归档解档

Animal *animal1 = [[Animal alloc] init];
animal1.age = @"1";
animal1.name = @"animal1";
animal1.sex = 1;
Dog *dog1 = [[Dog alloc] init];
dog1.age = @"1";
dog1.name = @"dog1";
dog1.sex = 1;
animal1.modelArray = [[NSMutableArray alloc] init];
[animal1.modelArray addObject:dog1];
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:animal1, nil];
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"t.data"];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array1 requiringSecureCoding:YES error:nil];
[data writeToFile:filePath atomically:YES];
NSData *data1 = [NSData dataWithContentsOfFile:filePath];
NSSet *classSet = [NSSet setWithObjects:[NSMutableArray class], [Animal class], [Dog class], nil];
NSError *error;
NSMutableArray *array2 = [NSKeyedUnarchiver unarchivedObjectOfClasses:classSet fromData:data1 error:&error];
NSLog(@"array1 address:%p, array2 address:%p", array1, array2);
for (Animal *animal in array1) {
  NSLog(@"animal:%p. modelArray:%p", animal, animal.modelArray);
}
NSLog(@"==========");
for (Animal *animal in array2) {
  NSLog(@"animal:%p. modelArray:%p", animal, animal.modelArray);
}

打印成果如下:

2021-01-12 15:10:39.873687+0800 ShallowCopy&DeepCopy[74371:28189240] array1 address:0x6000032ff810, array2 address:0x6000032e1e90
2021-01-12 15:10:39.873830+0800 ShallowCopy&DeepCopy[74371:28189240] animal:0x6000032fefd0. modelArray:0x6000032ff720
2021-01-12 15:10:39.873938+0800 ShallowCopy&DeepCopy[74371:28189240] ==========
2021-01-12 15:10:39.874059+0800 ShallowCopy&DeepCopy[74371:28189240] animal:0x6000032f2940. modelArray:0x6000032e1ec0

从打印成果能够看出模型中的数组也进行了深仿制,其实也很好了解,归档解档的原理便是将内存中的数据写入本地文件里,当再从本地文件中读出数据时肯定是要新拓荒出内存空间的。 假如模型嵌套层次较深时,能够运用归档解档进行深仿制。

留意一

运用目标的mutableCopy和数组运用initWithArray:copyItems时需要模型遵守NSCopying或NSMutableCopying协议并重写以下办法

- (id)copyWithZone:(NSZone *)zone {
    Animal *copy = [[Animal allocWithZone:zone] init];
    copy.name = [_name copyWithZone:zone];
    copy.age = [_age copyWithZone:zone];
    copy.dog = [_dog copyWithZone:zone];
    copy.modelArray = [_modelArray copyWithZone:zone];
    return copy;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
    Animal *copy = [[Animal allocWithZone:zone] init];
    copy.name = [_name mutableCopyWithZone:zone];
    copy.age = [_age mutableCopyWithZone:zone];
    copy.dog = [_dog mutableCopyWithZone:zone];
    copy.modelArray = [_modelArray copyWithZone:zone];
    return copy;
}

留意二

运用归档解档时需要遵守NSSecureCoding协议并重写以下办法

+ (BOOL)supportsSecureCoding {
    return YES;
}
- (id)initWithCoder:(NSCoder *)coder {
    if (self = [super init]) {
        self.name = [coder decodeObjectForKey:@"name"];
        self.age = [coder decodeObjectForKey:@"age"];
        self.sex = [coder decodeIntForKey:@"sex"];
        self.dog = [coder decodeObjectForKey:@"dog"];
        self.modelArray = [coder decodeObjectForKey:@"modelArray"];
    }
    return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.name forKey:@"name"];
    [coder encodeObject:self.age forKey:@"age"];
    [coder encodeInt:self.sex forKey:@"sex"];
    [coder encodeObject:self.dog forKey:@"dog"];
    [coder encodeObject:self.modelArray forKey:@"modelArray"];
}