IOS详解深拷贝和浅拷贝的概念、具体的使用以及对于容器类(NSArray,NSDictionary 等)和非容器类(NSString,NSdata 等)拷贝的差异

来源:互联网 发布:佳能网络打印机设置ip 编辑:程序博客网 时间:2024/06/04 19:14

本文首先先从介绍深拷贝和浅拷贝的概念问题开始,以便更好的理解和学习他们的使用环境.
使用深拷贝和浅拷贝的前提就是使用对象必须遵循NSCopying 或者 NSMutableCopying 协议,以下为截取自 NSArray 的头文件声明

浅拷贝的概念:浅拷贝就是对当前对象指针的拷贝;
深拷贝的概念:深拷贝是对当前对象的拷贝;
举个例子就是浅拷贝就是给当前对象拷贝了一个影子,当被拷贝对象值改变时,新拷贝出来的对象(影子)指向该对象,所以也会使你拷贝出来的对象改变值;
深拷贝就相当于给当前对象克隆了一个一模一样的对象,新拷贝出来的对象指针和内存地址都发生了变化,和原来的对象除了在数值上一样以外没有任何联系了,
被拷贝对象的值改变,拷贝出来的对象也不会受到影响了,就相当于克隆完之后你吃胖了,你克隆出来的那人还是那么瘦看着你笑一样的道理.

不知道注意到没有,我上面介绍概念的时候都是在用深拷贝和浅拷贝两个名词,并没有直接使用 cpoy 和 mutableCopy 两个名词,这是因为 copy并不是代表浅拷贝, mutableCopy 也不代表深拷贝,他们只是表明拷贝出来的对象是什么类型的,是可变还是不可变

下面详细说下cpoy 和 MutableCopy两个方法的区别:
讲解这两个方法我们分为两种情况一一去分析,最后再统一结果,这样更容易透彻的了解
一、对于不可变对象的 copy 和 mutableCopy分析
我们根据实际代码分析:
// 对于不可变字符串进行测试
NSString *string = @”asdfg”;

// copy 出可变和不可变两种字符串测试NSString *copyString = [string copy];NSMutableString *copyMutableString = [string copy];// mutableCopy 出可变和不可变两种字符串测试NSString *mutableCopyString = [string mutableCopy];NSMutableString *mutableCopyMutableString = [string mutableCopy];// 打印指针NSLog(@"========%p",string);NSLog(@"========%p",copyString);NSLog(@"========%p",copyMutableString);NSLog(@"========%p",mutableCopyString);NSLog(@"========%p",mutableCopyMutableString);

结果为:
2017-08-18 11:11:44.036 dailyTest[35812:7678186] ========0x100120160
2017-08-18 11:11:44.036 dailyTest[35812:7678186] ========0x100120160
2017-08-18 11:11:44.037 dailyTest[35812:7678186] ========0x100120160
2017-08-18 11:11:44.037 dailyTest[35812:7678186] ========0x610000075580
2017-08-18 11:11:44.037 dailyTest[35812:7678186] ========0x610000075480

结果可见:
对不可变对象使用 copy, 新拷贝出来的对象地址和被拷贝对象地址一致
对不可变对象使用 mutableCopy 新拷贝出来的对象和被拷贝的对象内存地址发生变化,
所以对不可变对象 copy 就是浅拷贝,mutableCopy 就是深拷贝.
同时需要注意:
NSMutableString *copyMutableString = [string copy];
在copyMutableString调用可变字符串方法的时候会发生崩溃,即使你在声明对象类型的时候声明成可变对象,但是使用 copy 方法拷贝出来的依旧是不可变对象,这也印证了我开头说的 copy 和 mutableCopy 只是代表拷贝出来的对象类型

二、对于可变对象的 copy 和 mutableCopy 分析
代码分析:
NSMutableString *mutableString = [[NSMutableString alloc] initWithString:@”uiipp”];

NSString *copyStringMutable = [mutableString copy];NSMutableString *copyMutableMutable = [mutableString copy];NSString *mutableCopyStringMutable = [mutableString mutableCopy];NSMutableString *mutableCopyMutableMutable = [mutableString mutableCopy];// 打印指针NSLog(@"********%p",mutableString);NSLog(@"********%p",copyStringMutable);NSLog(@"********%p",copyMutableMutable);NSLog(@"********%p",mutableCopyStringMutable);NSLog(@"********%p",mutableCopyMutableMutable);

结果:
2017-08-18 11:11:44.037 dailyTest[35812:7678186] ********0x100120200
2017-08-18 11:11:44.038 dailyTest[35812:7678186] ********0xa000070706969755
2017-08-18 11:11:44.038 dailyTest[35812:7678186] ********0xa000070706969755
2017-08-18 11:11:44.038 dailyTest[35812:7678186] ********0x600000078cc0
2017-08-18 11:11:44.038 dailyTest[35812:7678186] ********0x600000079400

结果分析对可变对象使用 copy 和MutableCopy 都会拷贝一个新的对象出来,即,copy 和 mutableCopy 都是深拷贝;

其他数据类型的结果分析和 NSString NSMutableString 一致,对于不可变类型 copy 是浅拷贝 ,mutableCopy 是深拷贝
对于可变类型都是深拷贝;

下面进行再深一层次的分析,对容器类例如(NSArray,NSDictionary )等,虽然以上结果依然成立,但是无论是浅拷贝还是深拷贝都只是相对于对象本身来说的,对于对象包含的元素并不进行拷贝,下面只测试数组一种类型的,其他相同:
测试代码:
// 不可变数组
NSMutableString *mutableString = [[NSMutableString alloc] initWithString:@”a”];
NSArray *array = @[mutableString, @”b”, @”c”, @”d”, @”e”];

// 容器类 + 不可变 + copyNSArray *copyArray = [array copy];NSMutableArray *mutableCopyArray = [array mutableCopy];NSLog(@"内存地址 :%p", array);NSLog(@"内存地址 :%p", copyArray);NSLog(@"内存地址 :%p", mutableCopyArray);NSLog(@"元素内存地址 :%p", array[0]);NSLog(@"元素内存地址 :%p", copyArray[0]);NSLog(@"元素内存地址 :%p", mutableCopyArray[0]);[mutableString appendString:@"sxxxx"];NSLog(@"############%@",array);NSLog(@"############%@",copyArray);NSLog(@"############%@",mutableCopyArray);

打印结果:
内存地址 :0x608000075340
2017-08-18 17:11:42.847 dailyTest[39274:8529843] 内存地址 :0x608000075340
2017-08-18 17:11:42.847 dailyTest[39274:8529843] 内存地址 :0x6080002413b0
2017-08-18 17:11:42.847 dailyTest[39274:8529843] 元素内存地址 :0x6080000753c0
2017-08-18 17:11:42.847 dailyTest[39274:8529843] 元素内存地址 :0x6080000753c0
2017-08-18 17:11:42.847 dailyTest[39274:8529843] 元素内存地址 :0x6080000753c0
2017-08-18 17:11:42.847 dailyTest[39274:8529843] ############(
asxxxx,
b,
c,
d,
e
)
2017-08-18 17:11:42.847 dailyTest[39274:8529843] ############(
asxxxx,
b,
c,
d,
e
)
2017-08-18 17:11:42.848 dailyTest[39274:8529843] ############(
asxxxx,
b,
c,
d,
e
)
结果分析显示:对象无论是深拷贝还是浅拷贝,对于其元素来说都是浅拷贝,并不进行深拷贝,当元素值被修改后,所有指向该元素地址的容器对象值都会改变

如果想对容器中包含的元素也进行深拷贝,则比较复杂:
NSMutableString *mutableString = [[NSMutableString alloc] initWithString:@”hahhah”];

NSMutableDictionary *mutableDictionary = [[NSMutableDictionary alloc] init];[mutableDictionary setObject:mutableString forKey:@"key"];NSMutableArray *oldMutableArray = [[NSMutableArray alloc] initWithObjects:mutableDictionary, nil];NSMutableArray *newMutableArray = [[NSMutableArray alloc] initWithArray:oldMutableArray];NSMutableArray *copyItemMutableArray = [[NSMutableArray alloc] initWithArray:oldMutableArray copyItems:YES];// 做一遍归档接档NSData *data = [NSKeyedArchiver archivedDataWithRootObject:oldMutableArray];NSMutableArray *archiverArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];[mutableString appendString:@"asd********"];[mutableDictionary setObject:@"value" forKey:@"key_1"];NSLog(@"内存地址:$$$$$$$$$$$$$$%p", oldMutableArray);    NSLog(@"内存地址:$$$$$$$$$$$$$$%p", newMutableArray);NSLog(@"内存地址:$$$$$$$$$$$$$$%p", copyItemMutableArray);    NSLog(@"内存地址:$$$$$$$$$$$$$$%p", archiverArray);NSLog(@"元素内存地址:$$$$$$$$$$$$$$%p", oldMutableArray[0]);    NSLog(@"元素内存地址:$$$$$$$$$$$$$$%p", newMutableArray[0]);NSLog(@"元素内存地址:$$$$$$$$$$$$$$%p", copyItemMutableArray[0]);    NSLog(@"元素内存地址:$$$$$$$$$$$$$$%p", archiverArray[0]);NSLog(@"$$$$$$$$$$$$$$%@", oldMutableArray);    NSLog(@"$$$$$$$$$$$$$$%@", newMutableArray);NSLog(@"$$$$$$$$$$$$$$%@", copyItemMutableArray);    NSLog(@"$$$$$$$$$$$$$$%@", archiverArray);

打印结果:
2017-08-18 17:46:15.492 dailyTest[39505:8647738] 内存地址:

0x60800004d3b02017081817:46:15.492dailyTest[39505:8647738]:
0x60800004d020
2017-08-18 17:46:15.493 dailyTest[39505:8647738] 内存地址:
0x60800004ca202017081817:46:15.493dailyTest[39505:8647738]:
0x60800004cae0
2017-08-18 17:46:15.493 dailyTest[39505:8647738] 元素内存地址:
0x60800004d0b02017081817:46:15.493dailyTest[39505:8647738]:
0x60800004d0b0
2017-08-18 17:46:15.493 dailyTest[39505:8647738] 元素内存地址:
0x6080000798402017081817:46:15.493dailyTest[39505:8647738]:
0x60800004d350
2017-08-18 17:46:15.493 dailyTest[39505:8647738]
(key="hahhahasd";"key1"=value;)2017081817:46:15.493dailyTest[39505:8647738]
(
{
key = “hahhahasd********”;
“key_1” = value;
}
)
2017-08-18 17:46:15.493 dailyTest[39505:8647738]
(key="hahhahasd";)2017081817:46:15.494dailyTest[39505:8647738]
(
{
key = hahhah;
}
)

通过打印结果知道:
- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag;方法只是对容易包含的第一层元素做一个深拷贝,
但是对于元素更深层次的数据则不会再做拷贝,所以也不是完全意义上的深拷贝,

通过归档解档方式实现可发现, string 改变后数组元素值没发生改变,并且元素字典设置新元素后,元素值也没改变,说明实现的是完全意义的深拷贝

以上代码都经过本人验证,
由于是自己一边学习一边测试整理,所以话语组织能力还有待提高,望谅解.
如有任何疑问或者讲解的地方有错误,欢迎提出,十分感谢

阅读全文
1 0