首先,我们应该知道:
copy: 获得不可变对象;
mutableCopy: 获得可变对象;
无论是深拷贝还是浅拷贝,以上都成立。
copy与retain的区别:
(1)copy是创建一个新对象,retain是创建一个指针,引用对象计数加1。Copy属性表示两个对象内容相同,新的对象retain为1 ,与旧有对象的引用计数无关,旧有对象没有变化。copy减少对象对上下文的依赖。
(2)retain属性表示两个对象地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1也就是说,retain 是指针拷贝,copy 是内容拷贝。
不同对象的深复制和浅复制
1、对于非容器对象,如:NSString、NSNumber、NSMutableString等等
(1)如果是不可变对象,copy是指针复制(浅拷贝),mutableCopy是对象复制(深拷贝)。
//指针拷贝(浅拷贝)
NSString *string = @"this is copy test";
NSString *stringCopy = [string copy];
//对象拷贝(深拷贝)
NSString *string = @"this is copy test";
NSMutableString *stringMCopy = [string mutableCopy];
(2)如果是可变对象,copy,mutableCopy两者都是对象复制(深复制);
//对象拷贝(深拷贝)
NSMutableString *string = [NSMutableString stringWithString:@"this is test"];
NSString *stringCopy = [string copy];
//对象拷贝(深拷贝)
NSMutableString *string = [NSMutableString stringWithString:@"this is test"];
NSMutableString *mStringCopy = [string mutableCopy];
2、对于容器对象,如NSArray 、NSDictionary、NSMutableArray、NSMutableDictionary等等
对于容器本身,非容器的情况也是适用的。需要探讨的是复制后容器内对象的变化。
(1)对于不可变对象
//指针拷贝(浅拷贝)
NSArray *array = [NSArray arrayWithObjects:@"a",@"b",@"c", nil];
NSArray *copyArray = [array copy];
//对象拷贝(深拷贝)。
NSArray *mCopyArray = [array mutableCopy];
(2)对于可变对象
//对象拷贝(深拷贝)
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:@"a",@"b",@"c", nil];
NSArray *copyMArray = [mArray copy];
//对象拷贝(深拷贝)
NSArray *mCopyMArray = [mArray mutableCopy];
(3)无论容器做何种拷贝,无论容器是可变还是不可变,对于容器内对象,都是指针拷贝(浅拷贝)。如何判断容器内对象是指针拷贝?只要打印出地址值即可:
NSLog(@"%p",[array objectAtIndex:0]); //0x96184
NSLog(@"%p",[copyArray objectAtIndex:0]); //0x96184
NSLog(@"%p",[mCopyArray objectAtIndex:0]); //0x96184
NSLog(@"%p",[mArray objectAtIndex:0]); //0xaf134
NSLog(@"%p",[copyMArray objectAtIndex:0]); //0xaf134
NSLog(@"%p",[mCopyMArray objectAtIndex:0]); //0xaf134
以上例子,说明容器内对象都为指针拷贝。当然,每次运行程序,地址值都是随机的。但是只要是指针拷贝,容器中的各个对象对应地址值都是一致,学过程序的各位大胸弟都是清楚的,这里不赘叙。
(4)如果真的需要对容器内对象做对象拷贝(深拷贝),有如下例子:
NSArray *arr = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c" ,nil];
//方法一
NSArray *deepCopyArr = [[NSArray alloc]initWithArray:arr copyItems:YES];
//方法二
NSArray *trueDeepCopyArr = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:arr]];
NSLog(@"%p",[arr objectAtIndex:0]); //0x1663bb90
NSLog(@"%p",[deepCopyArr objectAtIndex:0]); //0x1663ba50
NSLog(@"%p",[trueDeepCopyArr objectAtIndex:0]); //0x1663c410
NSLog(@"%p",[arr objectAtIndex:1]); //0x7c144
NSLog(@"%p",[deepCopyArr objectAtIndex:1]); //0x7c144
NSLog(@"%p",[trueDeepCopyArr objectAtIndex:1]); //0x1663c2f0
从以上例子可以看出,deepCopyArr不是真正的深拷贝,对于容器内对象,
比如[arr objectAtIndex:0],为可变对象,为对象拷贝(深拷贝)。
比如[arr objectAtIndex:1],为不可变对象,为指针拷贝(浅拷贝)。
而trueDeepCopyArr无论是容器内对象可变还是不可变,都为对象拷贝(深拷贝)。
为何是这样?对于不可变对象,如果拿到这个对象,却不能修改,所以只需要指针拷贝,即使能够对象拷贝,拿到的对象是一样的,没法修改。所以指针拷贝即可,无需对象拷贝,也就不是真正意义上 的深拷贝。
方法二,则实现完全的对象拷贝(深拷贝)。
3、自定义对象的拷贝
自定义对象需要遵循两个协议:NSCopying、NSMutableCopying
下面提供一个例子:
Person.h:
#import <UIKit/UIKit.h>
@interface Person :
UIViewController<NSCopying,NSMutableCopying>
@property (nonatomic,retain)NSMutableString *name;
@property (nonatomic,retain)NSString *lolName;
@property (nonatomic)int age;
@end
Person.m:
#import "Person.h"
@implementation Person
-(id)init{
self = [super init];
if (self) {
self.name = [[NSMutableString alloc]init];
self.lolName = [[NSString alloc]init];
self.age = 1;
}
return self;
}
-(id)copyWithZone:(NSZone *)zone{
Person *copyPerson = [[self.class allocWithZone:zone]init];
copyPerson.name = self.name;
copyPerson.lolName = self.lolName;
copyPerson.age = self.age;
return copyPerson;
}
-(id)mutableCopyWithZone:(NSZone *)zone{
Person *copyPerson = [[self.class allocWithZone:zone]init];
copyPerson.name = [_name mutableCopy];
copyPerson.lolName = [_lolName copy];
copyPerson.age = _age;
return copyPerson;
}
@end
测试代码:
Person *myself = [[Person alloc]init];
[myself.name appendString:@"mk"];
myself.lolName = @"cleanlove";
myself.age = 24;
NSLog(@"原装:%p",myself);//0x15632a70
Person *copyMyself = [myself copy];
NSLog(@"复制:%p",copyMyself);//0x1553d730
Person *copyMyself2 = [myself mutableCopy];
NSLog(@"复制:%p",copyMyself2);//0x1553dcf0
从以上例子可以看出,对于自定义对象的copy和mutableCopy,均为对象拷贝(深拷贝)。而对于对象中的属性是何种拷贝,则需要根据以上第1点,对于可变对象、不可变对象进行判断。
ps:
深拷贝(对象拷贝、深复制)
浅拷贝(指针拷贝、浅复制)