iOS - 深拷贝浅拷贝

2022-07-18  本文已影响0人  搬砖的crystal

一、基本概念

浅拷贝

浅拷贝只是对 object 对象指针进行拷贝,不会开辟新的内存。与数据源指向的是同一内存。例如 copyA = [A copy]capyAA 指向的是同一内存,A 的值变化,copyA 的值也会发生变化。
copy 方法做的是浅拷贝。

深拷贝

深拷贝会开辟新的内容,然后对原数据进行复制,新 object 对象与数据源指向的是不同的内存,对数据源操作不会影响新object 对象。例如 mutableCopyA = [A mutableCopy]mutableCopyAA 指向的是两块不同的内存,A 的值发生变化不会影响到 mutableCopyA 的值。
mutableCopy 方法做的是深拷贝。
�####二、不同对象类型的深拷贝和浅拷贝

1.NSStringNSMutableString
NSString
    // 不可变字符串
    NSString *str = [NSString stringWithFormat:@"哈哈哈哈哈"];
    NSLog(@"str = %@ :对象地址==%p ,对象指针地址==%p",str ,str, &str);
    NSString *strCopy = [str copy];
    NSString *strMutableCopy = [str mutableCopy];
    str = [NSString stringWithFormat:@"测试"];

    NSLog(@"str = %@ :对象地址==%p ,对象指针地址==%p",str ,str, &str);
    NSLog(@"strCopy = %@ :对象地址==%p , 对象指针地址==%p", strCopy,strCopy, &strCopy);
    NSLog(@"mutalbeCopy = %@ :对象地址==%p , 对象指针地址==%p",strMutableCopy, strMutableCopy, &strMutableCopy);

输出结果

2022-07-18 15:38:47.952442+0800 DJTestDemo[28524:358667] str = 哈哈哈哈哈 :对象地址==0x60000394f540 ,对象指针地址==0x7ff7bdb61b38
2022-07-18 15:38:47.952607+0800 DJTestDemo[28524:358667] str = 测试 :对象地址==0x600003715ae0 ,对象指针地址==0x7ff7bdb61b38
2022-07-18 15:38:51.508690+0800 DJTestDemo[28524:358667] strCopy = 哈哈哈哈哈 :对象地址==0x60000394f540 , 对象指针地址==0x7ff7bdb61b30
2022-07-18 15:38:51.508918+0800 DJTestDemo[28524:358667] mutalbeCopy = 哈哈哈哈哈 :对象地址==0x60000394f300 , 对象指针地址==0x7ff7bdb61b28

针对 NSString 类型来看,copy 后对象地址没有变化,说明copy 只是创建了新的指针,指针指向的还是原来的内存地址。mutalbeCopy 之后对象地址也发生了变化,说明 mutalbeCopy 创建了新的指针,同时开辟了新的内存并将 string 的内容复制了过去。

NSMutableString
    // 可变字符串
    NSMutableString *mutableString = [NSMutableString stringWithString:@"hello world"];
    NSString *mutableStringCopy = [mutableString copy];
    NSMutableString *mutableStringMutableCopy = [mutableString mutableCopy];

    NSLog(@"mutableString: 对象地址==%p , 对象指针地址==%p", mutableString, &mutableString);
    NSLog(@"copy: 对象地址==%p , 对象指针地址==%p", mutableStringCopy, &mutableStringCopy);
    NSLog(@"mutalbeCopy: 对象地址==%p , 对象指针地址==%p", mutableStringMutableCopy, &mutableStringMutableCopy);

输出结果

2022-07-18 16:00:45.717067+0800 DJTestDemo[30814:386885] mutableString: 对象地址==0x60000233a6d0 , 对象指针地址==0x7ff7be10cca8
2022-07-18 16:00:45.717308+0800 DJTestDemo[30814:386885] copy: 对象地址==0x600002d10380 , 对象指针地址==0x7ff7be10cca0
2022-07-18 16:00:45.717418+0800 DJTestDemo[30814:386885] mutalbeCopy: 对象地址==0x60000233aa30 , 对象指针地址==0x7ff7be10cc98

针对 NSMutableString 类型来看,不论是 copy 还是 mutalbeCopy,对象的指针地址和对象地址都发生了变化,因此两种形式都开辟了新内存并将内容复制到的新内存中。

2. NSArrayNSMutableArray

按照上面 NSStringNSMutableString 系统非集合类对象的思路,对 NSArrayNSMutableArray 系统集合类对象做同样的示例分析。
因为集合类对象里面的元素还可以是对象,因此需要打印内部元素的地址信息,查看是否进行了深拷贝。

NSArray
    NSArray *cellArray1 = @[@"1", @"2", @"3"];
    NSArray *cellArray2 = @[@"4", @"5", @"6"];
    
    NSArray *array = @[cellArray1, cellArray2];
    NSArray *arrayCopy = [array copy];
    NSArray *arrayMutableCopy = [array mutableCopy];
        
    NSArray *tempArray = array.firstObject;
    NSArray *tempArrayCopy = arrayCopy.firstObject;
    NSArray *tempArrayMutableCopy = arrayMutableCopy.firstObject;
    

    NSLog(@"      array:对象地址 == %p ,对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", array, &array, tempArray, &tempArray);
    NSLog(@"       copy: 对象地址 == %p ,对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", arrayCopy, &arrayCopy, tempArrayCopy, &tempArrayCopy);
    NSLog(@"mutalbeCopy: 对象地址 == %p ,对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", arrayMutableCopy, &arrayMutableCopy, tempArrayMutableCopy, &tempArrayMutableCopy);

输出结果

2022-07-18 16:13:17.254829+0800 DJTestDemo[32009:406468]       array:对象地址 == 0x600002343180 ,对象指针地址 == 0x7ff7b3413b10 , firstObject地址 == 0x10caf4c18 , firstObject指针地址 == 0x7ff7b3413af8
2022-07-18 16:13:17.254992+0800 DJTestDemo[32009:406468]        copy: 对象地址 == 0x600002343180 ,对象指针地址 == 0x7ff7b3413b08 , firstObject地址 == 0x10caf4c18 , firstObject指针地址 == 0x7ff7b3413af0
2022-07-18 16:13:17.255115+0800 DJTestDemo[32009:406468] mutalbeCopy: 对象地址 == 0x600002d1d470 ,对象指针地址 == 0x7ff7b3413b00 , firstObject地址 == 0x10caf4c18 , firstObject指针地址 == 0x7ff7b3413ae8
NSMutableString
    NSArray *cellArray1 = @[@"1", @"2", @"3"];
    NSArray *cellArray2 = @[@"4", @"5", @"6"];
    NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:@[cellArray1, cellArray2]];
    NSArray *mutableArrayCopy = [mutableArray copy];
    NSMutableArray *mutableArrayMutableCopy = [mutableArray mutableCopy];
    
    NSMutableArray *tempMutableArray = mutableArray.firstObject;
    NSMutableArray *tempMutableArrayCopy = mutableArrayCopy.firstObject;
    NSMutableArray *tempMutableArrayMutableCopy = mutableArrayMutableCopy.firstObject;
    
    NSLog(@"mutableArray: 对象地址 == %p , 对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", mutableArray, &mutableArray, tempMutableArray, &tempMutableArray);
    NSLog(@"        copy: 对象地址 == %p , 对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", mutableArrayCopy, &mutableArrayCopy, tempMutableArrayCopy, &tempMutableArrayCopy);
    NSLog(@" mutalbeCopy: 对象地址 == %p , 对象指针地址 == %p , firstObject地址 == %p , firstObject指针地址 == %p", mutableArrayMutableCopy, &mutableArrayMutableCopy, tempMutableArrayMutableCopy, &tempMutableArrayMutableCopy);

输出结果

2022-07-18 16:18:30.292810+0800 DJTestDemo[32109:412361] mutableArray: 对象地址 == 0x6000022bcd80 , 对象指针地址 == 0x7ff7b9f8db10 , firstObject地址 == 0x105f7ac20 , firstObject指针地址 == 0x7ff7b9f8daf8
2022-07-18 16:18:30.292948+0800 DJTestDemo[32109:412361]         copy: 对象地址 == 0x600002cf3520 , 对象指针地址 == 0x7ff7b9f8db08 , firstObject地址 == 0x105f7ac20 , firstObject指针地址 == 0x7ff7b9f8daf0
2022-07-18 16:18:30.293075+0800 DJTestDemo[32109:412361]  mutalbeCopy: 对象地址 == 0x6000022bcdb0 , 对象指针地址 == 0x7ff7b9f8db00 , firstObject地址 == 0x105f7ac20 , firstObject指针地址 == 0x7ff7b9f8dae8
结论

copymutableCopy 之后对象地址的变化与非集合对象变化相同。
无论是 copy 还是 mutableCopy,内部元素的地址都没有变化,只是指针地址发生了变化。
mutableCopy做的也只是浅层深拷贝。

3.自定义对象的深拷贝和浅拷贝

首先自定义对象需要实现 NSCopyingNSMutableCopying 协议。

#import <Foundation/Foundation.h>
@interface DJExample : NSObject
@property (nonatomic, strong) NSString *exampleTitle;
@end
#import "DJExample.h"
@interface DJExample()<NSCopying,NSMutableCopying>

@end
@implementation DJExample

// 实现copyWithZone方法
- (id)copyWithZone:(NSZone *)zone {
    DJExample *example = [[self class] allocWithZone:zone];
    example.exampleTitle = self.exampleTitle;
    return example;
}

// 实现mutableCopyWithZone方法
- (id)mutableCopyWithZone:(NSZone *)zone {
    DJExample *example = [[self class] allocWithZone:zone];
    example.exampleTitle = self.exampleTitle;
    return example;
}

@end
    DJExample *example1 = [[DJExample alloc] init];
    example1.exampleTitle = @"test1";
    
    DJExample *exampleCopy = [example1 copy];
    DJExample *exampleMutableCopy = [example1 mutableCopy];
    
    NSString *title = example1.exampleTitle;
    NSString *titleCopy = exampleCopy.exampleTitle;
    NSString *titleMutableCopy = exampleMutableCopy.exampleTitle;
    


    NSLog(@"    Example: 对象地址 == %p ,对象指针地址 == %p , 属性地址 == %p ,属性指针地址 == %p", example1, &example1, title, &title);
    NSLog(@"       copy: 对象地址 == %p ,对象指针地址 == %p , 属性地址 == %p ,属性指针地址 == %p", exampleCopy, &exampleCopy, titleCopy, &titleCopy);
    NSLog(@"mutalbeCopy: 对象地址== %p ,对象指针地址 == %p , 属性地址 == %p ,属性指针地址 == %p", exampleMutableCopy, &exampleMutableCopy, titleMutableCopy, &titleMutableCopy);

输出结果

2022-07-18 16:42:58.667753+0800 DJTestDemo[32713:434463]     Example: 对象地址 == 0x6000034883a0 ,对象指针地址 == 0x7ff7b90b4b38 , 属性地址 == 0x106e4d218 ,属性指针地址 == 0x7ff7b90b4b20
2022-07-18 16:42:58.667900+0800 DJTestDemo[32713:434463]        copy: 对象地址 == 0x6000034883b0 ,对象指针地址 == 0x7ff7b90b4b30 , 属性地址 == 0x106e4d218 ,属性指针地址 == 0x7ff7b90b4b18
2022-07-18 16:42:58.668013+0800 DJTestDemo[32713:434463] mutalbeCopy: 对象地址== 0x6000034883c0 ,对象指针地址 == 0x7ff7b90b4b28 , 属性地址 == 0x106e4d218 ,属性指针地址 == 0x7ff7b90b4b10

自定义对象使用 copymutableCopy 之后,对象的地址和指针地址都发生了变化,但是属性地址还是指向原来的地址,也就是说实际也是浅层的深拷贝。
因为 copyWithZone 方法和 mutableCopyWithZone 都是自己重写的,所以其实自定义对象使用 copy 还是 mutableCopy 并没有什么区别。

4.数组中的元素是自定义对象时的深拷贝和浅拷贝
    DJExample *example2 = [[DJExample alloc] init];
    example2.exampleTitle = @"test2";
    
    NSArray *exampleArray = [[NSArray alloc] initWithObjects:example2, nil];
    NSArray *shallowCopyArray = [[NSArray alloc] initWithArray:exampleArray];
    NSArray *deepCopyArray = [[NSArray alloc] initWithArray:exampleArray copyItems:YES];
    
    DJExample *shallowCopyExample = shallowCopyArray.firstObject;
    DJExample *deepCopyExample = deepCopyArray.firstObject;
    
    example2.exampleTitle = @"where is test2";

    NSLog(@"    exampleArray:对象地址 == %p , 对象指针地址 == %p , 属性地址 == %p , 属性指针地址 == %p , 属性值 == %@", exampleArray, &exampleArray, example2, &example2, example2.exampleTitle);
    NSLog(@"shallowCopyArray: 对象地址 == %p , 对象指针地址 == %p , 属性地址 == %p , 属性指针地址 == %p , 属性值 == %@", shallowCopyArray, &shallowCopyArray, shallowCopyExample, &shallowCopyExample, shallowCopyExample.exampleTitle);
    NSLog(@"   deepCopyArray: 对象地址 == %p , 对象指针地址 == %p , 属性地址 == %p , 属性指针地址 == %p , 属性值 == %@", deepCopyArray, &deepCopyArray, deepCopyExample, &deepCopyExample, deepCopyExample.exampleTitle);

输出结果

2022-07-18 16:51:22.639103+0800 DJTestDemo[32916:442954]     exampleArray:对象地址 == 0x600003298d20 , 对象指针地址 == 0x7ff7b9279b30 , 属性地址 == 0x6000030b0190 , 属性指针地址 == 0x7ff7b9279b38 , 属性值 == where is test2
2022-07-18 16:51:22.639313+0800 DJTestDemo[32916:442954] shallowCopyArray: 对象地址 == 0x600003298d20 , 对象指针地址 == 0x7ff7b9279b28 , 属性地址 == 0x6000030b0190 , 属性指针地址 == 0x7ff7b9279b18 , 属性值 == where is test2
2022-07-18 16:51:22.639472+0800 DJTestDemo[32916:442954]    deepCopyArray: 对象地址 == 0x6000030b01d0 , 对象指针地址 == 0x7ff7b9279b20 , 属性地址 == 0x6000030b0160 , 属性指针地址 == 0x7ff7b9279b10 , 属性值 == test2

从运行结果中可以看出,使用 initWithArray: 方法实现的是浅拷贝,原对象的值发生改变新数组中也会同步改变。
使用 initWithArray: copyItems: 方法实现的是深拷贝,数组和数组中的对象都会开辟新的内存,因此原数组中的数据发生变化,新数组不会变化。数组中的自定义对象需要实现NSCopying 和 NSMutableCopying 协议。

上一篇下一篇

猜你喜欢

热点阅读