Objective-C 浅拷贝与深拷贝

一个Objective-C对象通常分配在堆上,并有一个或者多个指针指向它。如下代码及其关系图所示:

NSObject *obj1 = [[NSObject alloc] init];
NSObject *obj2 = obj1; 

 

所谓浅拷贝与深拷贝,需要分为非容器类(如NSString)与容器类(如NSArray)来理解。如下表格所示:

 

与拷贝相关的两个系统函数是copy和mutableCopy,它们分别实现的是浅拷贝还是深拷贝呢?下面通过代码来验证:

不可变非容器:
NSString *str1 = @"this is a string.";
NSString *str2 = [str1 copy]; // 浅拷贝
NSString *str3 = [str1 mutableCopy]; // 深拷贝
NSLog(@"str1 address[%p] content[%@]", str1, str1);
NSLog(@"str2 address[%p] content[%@]", str2, str2);
NSLog(@"str3 address[%p] content[%@]", str3, str3);

输入:
str1 address[0x102fc8058] content[this is a string.]
str2 address[0x102fc8058] content[this is a string.]
str3 address[0x6040002500e0] content[this is a string.]

 

可变非容器:
NSMutableString *str4 = [[NSMutableString alloc] initWithString:@"this is a string."];
NSMutableString *str5 = [str4 copy]; // 深拷贝
NSMutableString *str6 = [str4 mutableCopy]; // 深拷贝
NSLog(@"str4 address[%p] content[%@]", str4, str4);
NSLog(@"str5 address[%p] content[%@]", str5, str5);
NSLog(@"str6 address[%p] content[%@]", str6, str6);

输出:
str4 address[0x60800024efd0] content[this is a string.]
str5 address[0x608000257dc0] content[this is a string.]
str6 address[0x608000257d90] content[this is a string.]

 

不可变容器:
NSArray *arr1 = @[[NSView new], [NSView new], [NSView new]];
NSArray *arr2 = [arr1 copy]; // 浅拷贝
NSArray *arr3 = [arr1 mutableCopy]; // 浅拷贝
NSLog(@"arr1 address[%p] content[%@]", arr1, arr1);
NSLog(@"arr2 address[%p] content[%@]", arr2, arr2);
NSLog(@"arr3 address[%p] content[%@]", arr3, arr3);

输出:
arr1 address[0x60800004f9c0] content[(
    "<NSView: 0x6080001212c0>",
    "<NSView: 0x608000121040>",
    "<NSView: 0x6080001210e0>"
)]
arr2 address[0x60800004f9c0] content[(
    "<NSView: 0x6080001212c0>",
    "<NSView: 0x608000121040>",
    "<NSView: 0x6080001210e0>"
)]
arr3 address[0x60800004c120] content[(
    "<NSView: 0x6080001212c0>",
    "<NSView: 0x608000121040>",
    "<NSView: 0x6080001210e0>"
)]

 

可变容器:
NSMutableArray *arr4 = [[NSMutableArray alloc] initWithArray:@[[NSView new], [NSView new], [NSView new]] ];
NSMutableArray *arr5 = [arr4 copy]; // 浅拷贝
NSMutableArray *arr6 = [arr4 mutableCopy]; // 浅拷贝
NSLog(@"arr4 address[%p] content[%@]", arr4, arr4);
NSLog(@"arr5 address[%p] content[%@]", arr5, arr5);
NSLog(@"arr6 address[%p] content[%@]", arr6, arr6);

输出:
arr4 address[0x6000000599e0] content[(
    "<NSView: 0x600000121a40>",
    "<NSView: 0x600000121ae0>",
    "<NSView: 0x600000121b80>"
)]
arr5 address[0x6000000599b0] content[(
    "<NSView: 0x600000121a40>",
    "<NSView: 0x600000121ae0>",
    "<NSView: 0x600000121b80>"
)]
arr6 address[0x600000059980] content[(
    "<NSView: 0x600000121a40>",
    "<NSView: 0x600000121ae0>",
    "<NSView: 0x600000121b80>"
)]

 

可以看到,无论是copy,还是mutableCopy,都不会拷贝容器中的对象。另外,懂C++的同学可能注意到,Objective-C的数组地址并不是数组首个元素的地址。  

注意:可以不拘泥于浅拷贝和深拷贝的概念,理解其中的原理即可。 

 

自定义的类,如果要使用copy方法,就要实现NSCopying协议中的方法:

- (id)copyWithZone:(NSZone*)zone; // zone参数没有用上,这是以前用来划分不同内存区的

类似,如果要使用mutableCopy方法,则要实现NSMutableCopying协议中的方法:

- (id)mutableCopyWithZone:(NSZone*)zone;

 

posted @ 2018-09-02 09:32  happyyoung  阅读(248)  评论(0编辑  收藏  举报