或许你在iOS开发的过程中很少用到copy or mutablecopy,但是有的时候阅读一些源码你会遇到,然后你草率的觉得自己懂了--在心里说so esay,但是当第二天的闹钟叫醒你去挤地铁时,你却什么也想不起来。废话说多了,这里并不是什么高超的技术难题需要我们攻克,只是理解记住。Just do it.
copy就是我们理解的复制,拷贝一个对象去创建一个新的对象拥有原来对象的相同的类型相同的属性。什么时候我们会需要去copy一个对象呢?
NSMutableString *mutableTest1 = [[NSMutableString alloc]initWithString:@"我是可变字符串"];
NSString *test2 = mutableTest1;
这个时候如果我们用test2去做一些事情那么它就是不安全的,因为随时mutableTest1有可能修改他的值,但我们仅仅想用他的值做我们的事情,这个时候就需要我们的copy。
了解浅拷贝(Shallow-copy)和深拷贝(Deep-copy)
拷贝一个对象可以浅拷贝也可以深拷贝,他们的相同点是都是直接复制他们的属性,不同点是怎么处理指针的引用。深拷贝直接复制所引用的对象而浅拷贝则复制对象的引用。
因此,如果对象A被浅拷贝到对象B,则对象B引用对象A所指向的相同的实例变量(或属性)。 深复制对象优先于浅复制,特别是对于值对象。深拷贝则新建一个对象,他们是不同的对象。拷贝同样会使引用计数+1,所以当你拷贝一个对象的时候有责任去释放他。
非集合对象的拷贝
顾名思义不是指集合类(数组,字典等)。首先一个对象能被拷贝需要遵循NSCopying协议并且实现了他的方法copyWithZone:,否则会在运行时的报错。如果是可变对象 遵循NSMutableCopying 协议并且实现 mutableCopyWithZone:来保证他的可变性。这时你可以利用copy和mutablecopy方法来进行复制操作,这两个方法会直接返回上面两个协议方法的返回值。
NSString *test1 = @"woshibukebian";
NSString *copytest1 = [test1 copy];
NSMutableString *copytest2 = [test1 copy];
NSMutableString *mutableCopytest1 = [test1 mutableCopy];
NSLog(@"%p,%@",test1,test1);
NSLog(@"%p,%@",copytest1,copytest1);
NSLog(@"%p,%@",copytest2,copytest2);
NSLog(@"%p,%@",mutableCopytest1,mutableCopytest1);
// [copytest2 appendString:@"hahhahahh"]; //报错
[mutableCopytest1 appendString:@"hahhahahh"];
NSLog(@"%p,%@",mutableCopytest1,mutableCopytest1);
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] test1,0x10a1bb068,woshibukebian
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] copytest1,0x10a1bb068,woshibukebian
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] copytest2,0x10a1bb068,woshibukebian
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] mutableCopytest1,0x608000072700,woshibukebian
2017-01-21 13:56:03.981 CopyDemo[25182:1130625] mutableCopytest1,0x608000072700,woshibukebianhahhahahh
对于不可变的对象copy方法只是复制了对象的引用,copytest1复制了test1对象的引用,即copytest1和test1指向相同的对象保存了相同的指针,属于浅拷贝。不可变的对象mutableCopy方法,则复制了引用的对象,重新创建了内存来保存,并且对象拥有可变性。
不可变对象copy方法属于浅拷贝仅仅是指针复制,mutableCopy方法是深拷贝会重新创建一个内存复制引用的对象。
NSMutableString *test1 = [[NSMutableString alloc]initWithString:@"我是可变字符串"];
NSMutableString *copytest1 = [test1 copy];
NSMutableString *mutableCopytest1 = [test1 mutableCopy];
NSLog(@"test1,%p,%@",test1,test1);
NSLog(@"copytest1,%p,%@",copytest1,copytest1);
NSLog(@"copytest2,%p,%@",mutableCopytest1,mutableCopytest1);
[copytest1 appendString:@"11111"]; //报错
[mutableCopytest1 appendString:@"hahhahahh"];
NSLog(@"mutableCopytest1,%p,%@",mutableCopytest1,mutableCopytest1);
2017-01-21 14:16:01.663 CopyDemo[25602:1139306] test1,0x60800007b440,我是可变字符串
2017-01-21 14:16:01.664 CopyDemo[25602:1139306] copytest1,0x608000050770,我是可变字符串
2017-01-21 14:16:01.664 CopyDemo[25602:1139306] mutableCopytest1,0x60800007b780,我是可变字符串
2017-01-21 14:16:01.664 CopyDemo[25602:1139306] mutableCopytest1,0x60800007b780,我是可变字符串hahhahahh
对于可变对象不论是copy还是mutableCopy方法都是深拷贝,都是重新创建了对象,复制的是引用的对象。copy的对象不可变,mutableCopy保持可变性。
总结:不可变对象因为本身是不可变的所以仅仅复制对象的引用即指向对象的指针就可以。copy复制了对象的指针,mutableCopy拷贝了引用的对象创建了可变对象保存原来引用的对象。
集合对象的拷贝
最常见的拷贝是浅拷贝,创建一个新的集合对象和原来的对象共享集合元素。深拷贝创建一个新的集合并把原来的集合的元素添加到新的集合中
这有一些方法去创建一个浅拷贝,当你创建一个浅拷贝原来的对象收到retain消息同时指向对象的指针被复制到新的集合来保存。
NSArray *shallowCopyArray = [someArray copyWithZone:nil];
NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];
我们可以利用copyWithZone:方法来拷贝一个集合或mutableCopyWithZone:或者initWithArray:copyItems: 方法。
这里有两种方法来创建一个集合元素的深拷贝,你可以调用 initWithArray:copyItems:方法在第二个参数中传入YES。如果你调用这个方法来创建一个集合的深拷贝这时会向原来集合中的所有元素发送copyWithZone:方法。如果这个元素对象没有遵循NSCopying协议就会向我们前面描述的会在运行时报错,然而copyWithZone:仅仅会做浅拷贝。这种拷贝仅仅创建了一个一层深拷贝不是完全意义上的深拷贝( This kind of copy is only capable of producing a one-level-deep copy),仅仅复制了集合中元素的指针并不是完全意义上的深拷贝,如果集合元素是一个不可变对象这可以满足深拷贝,但如果是一个集合的集合那么集合元素同样有可能会发生变化。
如果您需要真正意义上的深拷贝你可以利用归档然后解归档的方法来进行深拷贝。
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
但你拷贝一个集合,该集合和集合中的元素的可变性有可能会受到一些影响,不同的方法对应不同影响。
- copyWithZone: 仅仅是表面的不可变,所有深层的可变性任然保留。
- initWithArray:copyItems: with NO 赋予表面的可变性和原来创建的一样,但是深层的可变性任然保持。
- initWithArray:copyItems: with YES 赋予表面的可变性和原来创建的一样,但是下一层的为不可变,所有的更深的层次保持原来的可变性。
- 归档和解归档会保留原来的所有的可变性,因为创建了全新的对象保留了原来的数据与他的可变性。
希望到现在能对copy有了更深入的了解,当然我的文字功底不好或解释与理解有偏差,希望大家指出。祝大家技术节节高!
努力的日子就是最美好的日子。——乔治亚·欧姬芙