浅拷贝:
Making a shallow copy:
NSArray *shallowCopyArray = [someArray copyWithZone:nil];
NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];
Deep Copies:这两种方法
If you create a deep copy of a collection in this way, each object in the collection is sent a copyWithZone: message. If the objects in the collection have adopted the NSCopying protocol, the objects are deeply copied to the new collection, which is then the sole owner of the copied objects. If the objects do not adopt the NSCopying protocol, attempting to copy them in such a way results in a runtime error. However, copyWithZone: produces a shallow copy.
Making a deep copy:
NSArray *deepCopyArray=[[NSArray alloc] initWithArray:someArray copyItems:YES];
A true deep copy:
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
当然在ios中并不是所有的对象都支持copy,mutableCopy,遵守NSCopying 协议的类可以发送copy消息,遵守NSMutableCopying 协议的类才可以发送mutableCopy消息。假如发送了一个没有遵守上诉两协议而发送 copy或者 mutableCopy,那么就会发生异常。但是默认的ios类并没有遵守这两个协议。如果想自定义一下copy 那么就必须遵守NSCopying,并且实现 copyWithZone: 方法,如果想自定义一下mutableCopy 那么就必须遵守NSMutableCopying,并且实现 mutableCopyWithZone: 方法。
id temp = [array objectAtIndex:0];
[array removeObjectAtIndex:0];
如果你再要使用temp就会出错,因为这个时候obj已经被释放了。
由于在程序中经常会遇到集合类的传值,所以,简单的retain未必够用,需要对集合内容的拷贝,也就是深拷贝。
1、 系统的非容器类对象:
这里指的是NSString,NSNumber等等一类的对象。
NSString *string = @"origion";
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
再看下面的例子:
NSMutableString *string = [NSMutableString stringWithString: @"origion"];
NSString *stringCopy = [string copy];
NSMutableString *mStringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
[mStringCopy appendString:@"mm"];//error
[string appendString:@" origion!"];
[stringMCopy appendString:@"!!"];
以上四个NSString对象所分配的内存都是不一样的。但是对于mStringCopy其实是个imutable对象,所以上述会报错。
对于系统的非容器类对象,我们可以认为,如果对一不可变对象复制,copy是指针复制(浅拷贝)和mutableCopy就是对象复制(深拷贝)。如果是对可变对象复制,都是深拷贝,但是copy返回的对象是不可变的。
string改变值,copy后的string并不会随着改变,还是原来的值。给string赋值也是一样的结果。
这篇文章中属性无论是可变还是不可变的,只要声明为strong,在可变对象赋值给属性后,如果改变可变对象,则属性也会改变。copy则是不改变。还是copy相对安全些。
2、 系统的容器类对象
指NSArray,NSDictionary等。对于容器类本身,上面讨论的结论也是适用的,需要探讨的是复制后容器内对象的变化。
//copy返回不可变对象,mutablecopy返回可变对象
NSArray *array1 = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
NSArray *arrayCopy1 = [array1 copy];
//arrayCopy1是和array1同一个NSArray对象(指向相同的对象),包括array里面的元素也是指向相同的指针。
NSMutableArray *mArrayCopy1 = [array1 mutableCopy];
//mArrayCopy1是array1的可变副本,指向的对象和array1不同,但是其中的元素和array1中的元素指向的是同一个对象。mArrayCopy1还可以修改自己的对象
[mArrayCopy1 addObject:@"de"];
[mArrayCopy1 removeObjectAtIndex:0];
array1和arrayCopy1是指针复制,而mArrayCopy1是对象复制,mArrayCopy1还可以改变期内的元素:删除或添加。但是注意的是,容器内的元素内容都是指针复制。
下面用另一个例子来测试一下。
NSArray *mArray1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *mArrayCopy2 = [mArray1 copy];
NSMutableArray *mArrayMCopy1 = [mArray1 mutableCopy];
//mArrayCopy2,mArrayMCopy1和mArray1指向的都是不一样的对象,但是其中的元素都是一样的对象——同一个指针
//一下做测试
NSMutableString *testString = [mArray1 objectAtIndex:0];
//testString = @"1a1";//这样会改变testString的指针,其实是将@“1a1”临时对象赋给了testString
[testString appendString:@" tail"];//这样以上三个数组的首元素都被改变了。
但是里面的元素指针地址都是一样的,无论是哪种拷贝。里面的元素都是浅复制。下面两个方法才是深复制,第二个才是真正意义的深复制。
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"],[NSString stringWithString:@"b"],@"c",nil];
NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject: array]];
trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则不是,对于deepCopyArray内的不可变元素其还是指针复制。或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。
拷贝构造:
当然在 ios 中并不是所有的对象都支持copy,mutableCopy,遵守NSCopying协议的类可以发送copy消息,遵守NSMutableCopying协议的类才可以发送mutableCopy消息。
假如发送了一个没有遵守上诉两协议而发送copy或者 mutableCopy,那么就会发生异常。但是默认的ios类并没有遵守这两个协议。如果想自定义一下copy 那么就必须遵守NSCopying,并且实现 copyWithZone: 方法,如果想自定义一下mutableCopy 那么就必须遵守NSMutableCopying,并且实现 mutableCopyWithZone: 方法。
如果是我们定义的对象,那么我们自己要实现NSCopying , NSMutableCopying这样就能调用copy和mutablecopy了。举个例子:
@interface MyObj : NSObject《NSCopying, NSMutableCopying》{
NSMutableString *_name;
NSString * _imutableStr ;
int _age;
}
@property (nonatomic, retain) NSMutableString *name;
@property (nonatomic, retain) NSString *imutableStr;
@property (nonatomic) int age;
copy拷贝构造:
-- (id)copyWithZone:(NSZone *)zone{
MyObj *copy = [[[self class] allocWithZone :zone] init];
copy->name = [_name copy];
copy->imutableStr = [_imutableStr copy];
copy->age = age;
return copy;
}
mutableCopy拷贝构造:
-- (id)mutableCopyWithZone:(NSZone *)zone{
MyObj *copy = NSCopyObject(self, 0, zone);
copy->name = [_name mutableCopy];
copy->age = age;
return copy;
}
http://www.jianshu.com/p/e6a7cdcc705d
注意点:
(1)当使用mutableCopy时,不管源对象是否可变,副本是可变的,并且实现真正意义上的拷贝。
当我们使用copy一个可变对象时,副本对象是不可变的。
(2)要想实现对象的自定义拷贝,必须实现NSCopying,NSMutableCopying协议,实现该协议的copyWithZone方法和mutableCopyWithZone方法。深拷贝和浅拷贝的区别就在于copyWithZone方法的实现。
字符串之所以用copy是因为:
NSMutableString *string1 = [NSMutableString stringWithFormat:@"大家好"];
FatherClass *father = [FatherClass new];
father.name = string1;
// 不能改变person.name的值,因为其内部copy新的对象
[string1 appendString:@" 哈哈"];
如果是copy,则father.name = @"大家好";
string1 = @"大家好哈哈"。如果是strong,father.name = string1= @"大家好哈哈" ;
深.浅拷贝
copy/mutableCopy NSString
NSString *string= @"汉斯哈哈哈";// 没有产生新对象NSString *copyString = [stringcopy];// 产生新对象NSMutableString *mutableCopyString = [stringmutableCopy];NSLog(@"string = %p copyString = %p mutableCopyString = %p",string, copyString, mutableCopyString);
copy/mutableCopy NSMutableString
NSMutableString*string = [NSMutableStringstringWithString:@"汉斯哈哈哈"];// 产生新对象NSString*copyString = [stringcopy];// 产生新对象NSMutableString*mutableCopyString = [string mutableCopy];NSLog(@"string = %p copyString = %p mutableCopyString = %p", string, copyString, mutableCopyString);
结论:
注意:其他对象NSArray、NSMutableArray 、NSDictionary、NSMutableDictionary一样适用
copy NSObject
HSPerson *p = [[HSPerson alloc] init];p.age =20;p.height =170.0;HSPerson *copyP = [p copy]; // 这里崩溃
崩溃:
看崩溃信息HSPerson应该先实现:
-(id)copyWithZone:(NSZone *)zone;
测试:
#import"HSPerson.h"@interfaceHSPerson()@end@implementationHSPerson- (id)copyWithZone:(NSZone*)zone{return@"汉斯哈哈哈";}@end
HSPerson *p = [[HSPerson alloc] init];p.age =20;p.height =170.0;HSPerson *copyP = [p copy];NSLog(@"copyP: %@", copyP);
可以看出copyWithZone重新分配新的内存空间,则:
- (id)copyWithZone:(NSZone *)zone{ HSPerson *person=[[HSPerson allocWithZone:zone]init]; returnperson;// 有些人可能下面alloc,重新初始化空间,但这方法已给你分配了zone,自己就无需再次alloc内存空间了// HSPerson *person=[[HSPerson alloc]init];}
HSPerson *p = [[HSPerson alloc] init];p.age =20;p.height =170.0;HSPerson *copyP = [p copy];NSLog(@"p = %p copyP = %p", p, copyP);NSLog(@"age = %d height = %f", copyP.age, copyP.height);
虽然copy了份新的对象,然而age,height值并未copy,那么:
- (id)copyWithZone:(NSZone *)zone{ HSPerson *person=[[HSPerson allocWithZone:zone]init];person.age = self.age;person.height = self.height; // 这里self其实就要被copy的那个对象,很显然要自己赋值给新对象,所以这里可以控制copy的属性 returnperson;}
这时你会想,有NSMutableCopying?没错,是有这货:
- (id)mutableCopyWithZone:(NSZone *)zone{ HSPerson *person=[[HSPerson allocWithZone:zone]init];person.age = self.age;person.height = self.height; returnperson;}
NSCopying、NSMutableCopying有啥区别?
其实感觉没必要有NSMutableCopying,因为压根就没可变的HSPerson,但如果该对象有其他行为,可以借用NSMutableCopying实现,哈哈哈
property里的copy、strong区别
说完深浅拷贝,理解property里的copy、strong就轻松多了!
copy
#import@interfaceHSPerson:NSObject@property(nonatomic,copy)NSString*name;@end
NSMutableString *string =[NSMutableString stringWithFormat:@"汉斯哈哈哈"];HSPerson *person=[[HSPerson alloc]init];person.name = string;// 不能改变person.name的值,因为其内部copy新的对象[string appendString:@" hans"]; NSLog(@"name = %@",person.name);
property copy 实际上就对name干了这个:
- (void)setName:(NSString *)name{_name= [namecopy];}
假设name为NSMutableString,会发生什么事?
@property(nonatomic,copy)NSMutableString*name;
这样会挨骂哦,实际上内部还是:
- (void)setName:(NSMutableString *)name{_name= [namecopy];}
copy出来的仍然是不可变字符!如果有人用NSMutableString的方法,就会崩溃:
strong
@property(nonatomic,strong)NSString*name;
NSMutableString *string =[NSMutableString stringWithFormat:@"汉斯哈哈哈"];HSPerson *person=[[HSPerson alloc]init];person.name = string;// 可以改变person.name的值,因为其内部没有生成新的对象[string appendString:@" hans"];NSLog(@"name = %@",person.name);