NSCopying/NSMutableCopying

来源:互联网 发布:淘宝平板版下载 编辑:程序博客网 时间:2024/05/19 23:57

1、几点说明

说到NSCopying和NSMutableCopying协议,不得不说的就是copy和mutableCopy。

  • 如果类想要支持copy操作,则必须实现NSCopying协议,也就是说实现copyWithZone方法;
  • 如果类想要支持mutableCopy操作,则必须实现NSMutableCopying协议,也就是说实现mutableCopyWithZone方法;
  • iOS系统中的一些类已经实现了NSCopying或者NSMutableCopying协议的方法,如果向未实现相应方法的系统类或者自定义类发送copy或者mutableCopy消息,则会crash。
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person copyWithZone:]: unrecognized selector sent to instance 0x6080000314c0'
  • 发送copy和mutableCopy消息,均是进行拷贝操作,但是对不可变对象的非容器类、可变对象的非容器类、可变对象的容器类、不可变对象的容器类中复制的方式略有不同;但如下两点是相同的:

  • 发送copy消息,拷贝出来的是不可变对象;

  • 发送mutableCopy消息,拷贝出来的是可变对象;
  • 故如下的操作会导致crash
NSMutableString *test1 = [[NSMutableString alloc]initWithString:@"11111"];NSMutableString  *test2 = [test1 copy];[test2 appendString:@"22222"];*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString appendString:]: unrecognized selector sent to

2、系统非容器类

系统提供的非容器类中,如NSString,NSMutableString,有如下特性:

  • 向不可变对象发送copy,进行的是指针拷贝;向不可变对象发送mutalbeCopy消息,进行的是内容拷贝;
NSString *test3 = @"111111";NSString *test4 = [test3 copy];NSMutableString *test5 = [test3 mutableCopy];NSLog(@"test3 is %p, test4 is %p, tast5 is %p",test3,test4,test5);test3 is 0x10d6bb3a8, test4 is 0x10d6bb3a8, tast5 is 0x600000073e80
  • 向可变对象发送copy和mutableCopy消息,均是深拷贝,也就是说内容拷贝;
NSMutableString *test11 = [[NSMutableString alloc]initWithString:@"444444"];NSString  *test12 = [test11 copy];    NSMutableString *test13 = [test11 mutableCopy];    NSLog(@"test11 is %p, test12 is %p, tast13 is %p",test11,test12,test13);test11 is 0x600000073e00, test12 is 0xa003434343434346, tast13 is 0x600000073dc0

3、系统容器类

系统提供的容器类中,如NSArray,NSDictionary,有如下特性:

  • 不可变对象copy,是浅拷贝,也就是说指针复制;发送mutableCopy,是深复制,也就是说内容复制;
NSArray *array = [NSArray arrayWithObjects:@"1", nil];NSArray *copyArray = [array copy];NSMutableArray *mutableCopyArray = [array mutableCopy];NSLog(@"array is %p,  copyArray is %p,  mutableCopyArray is %p", array, copyArray, mutableCopyArray);array is 0x60800001e580,  copyArray is 0x60800001e580,  mutableCopyArray is 0x608000046ea0
  • 可变对象copy和mutableCopy均是单层深拷贝,也就是说单层的内容拷贝(这个解释看最下面单层浅拷贝和完全拷贝)
NSMutableArray *element = [NSMutableArray arrayWithObject:@1];NSMutableArray *array = [NSMutableArray arrayWithObject:element];NSArray *copyArray = [array copy];NSMutableArray *mutableCopyArray = [array mutableCopy];NSLog(@"array is %p,  copyArray is %p,  mutableCopyArray is %p", array, copyArray, mutableCopyArray);[mutableCopyArray[0] addObject:@2];NSLog(@"element is %@,  array is %@,  copyArray is %@,  mutableCopyArray is %@", element,array,copyArray, mutableCopyArray);2017-02-22 11:53:25.286 test[91520:3915695] array is 0x600000057670,  copyArray is 0x600000000bc0,  mutableCopyArray is 0x6080000582a02017-02-22 11:53:25.287 test[91520:3915695] element is (1,2),  array is (    (    1,    2)),  copyArray is (    (    1,    2)),  mutableCopyArray is (    (    1,    2))

4、自定义的类

重要说明:
1、所以的代码设计均是针对业务需求。
2、对于自定义的类,决定能否向对象发送copy和mutableCopy消息也是如此;

1、@property 声明中用 copy 修饰

不得不说下copy和strong在复制时候的区别,此处不讲引用计数的问题。
copy:拷贝一份不可变副本赋值给属性;所以当原对象值变化时,属性值不会变化;
strong:有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性;

@interface Person : NSObject @property (nonatomic, copy) NSString *familyname;@property (nonatomic, strong) NSString *nickname;@endPerson *p1 = [[Person alloc]init];NSMutableString *familyname = [[NSMutableString alloc]initWithString:@"张三"];p1.familyname = familyname;[familyname appendString:@"峰"];NSLog(@"p1.familyname is %@",p1.familyname);NSMutableString *nickname = [[NSMutableString alloc]initWithString:@"二狗"];p1.nickname = nickname;[nickname appendString:@"蛋儿"];NSLog(@"p1.nickname is %@", p1.nickname);2017-02-22 13:53:58.979 test[98299:3978965] p1.familyname is 张三2017-02-22 13:53:58.979 test[98299:3978965] p1.nickname is 二狗蛋儿

2、类的对象的copy

此处唯一需要说明的一点就是注意类的继承。
这篇文章有非常清晰详细的说明,此处只照搬下结论:

  • 1 类直接继承自NSObject,无需调用[super copyWithZone:zone]
  • 2 父类实现了copy协议,子类也实现了copy协议,子类需要调用[super copyWithZone:zone]
  • 3 父类没有实现copy协议,子类实现了copy协议,子类无需调用[super copyWithZone:zone]
  • 4、copyWithZone方法中要调用[[[self class] alloc] init]来分配内存

5、NSCopying

  • NSCopying是对象拷贝的协议。
  • 类的对象如果支持拷贝,该类应遵守并实现NSCopying协议。
  • NSCopying协议中的方法只有一个,如下:
- (id)copyWithZone:(NSZone *)zone {        Person *model = [[[self class] allocWithZone:zone] init];    model.firstName = self.firstName;    model.lastName  = self.lastName;    //未公开的成员    model->_nickName = _nickName;    return model;}

2、NSMutableCopying

当自定义的类有一个属性是可变对象时,对此属性复制时要执行mutableCopyWithZone操作。

- (id)copyWithZone:(NSZone *)zone {    AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init];    serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone];    serializer.queryStringSerializationStyle = self.queryStringSerializationStyle;    serializer.queryStringSerialization = self.queryStringSerialization;    return serializer;}

5、参考文章:

http://www.jianshu.com/p/cfdbd4d7f162
http://blog.csdn.net/qq_18425655/article/details/51325921
https://www.oschina.net/question/2330001_2181415
https://www.zybuluo.com/MicroCai/note/50592



上面文章原文链接


在这里说一下浅拷贝、单层深拷贝和完全深拷贝==============================

写于前:

在之前转载的一片文章中,文中对浅复制和深复制进行了详细的解读,同时还提到了深复制(one-level-deep copy)、完全复制(true copy)的概念,并指出iOS开发中的深复制是单层深赋值,本文将对这几个概念进行验证梳理。

(单层和完全概念区分:例如多层数组只实现一层内容拷贝,其他层为指针拷贝成为单层深复制;若多层内容都实现拷贝称为完全赋值)


程序中用到的几点概念补充

(1)

浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制。 
深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制。 
完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制。


(2)

归档和解档的概念补充: 
有时存在这样的需求,即将程序中使用的多个对象及其属性值,以及它们的相互关系保存到文件中,或者发送给另外的进程。为了实现此功能,foundation框架中,可以把相互关联的多个对象归档为二进制文件,而且还能将对象的关系从二进制文件中还原出来。

归档:将对象打包成二进制文件。NSKeyedArchiver:归档器 
解档:归档的逆变换。NSKeyedUnarchiver:解档器 
因此可以利用归档和解档来实现完全复制


代码验证

    //创建多层数组    NSArray *array = @[@1,@2];    NSArray *oldArray = @[@"xxxx",array];    //浅复制    NSArray *shallowArray = [oldArray copy];    //深复制    NSArray *oneDeepLevelArray = [oldArray mutableCopy];    //完全深复制,利用归档和解档的方式    NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
    NSLog(@"%p,%p,%p,%p",oldArray,shallowArray,oneDeepLevelArray,trueDeepCopyArray);
  • 1
  • 1

输出结果:

0x7fc7e9c1c0d0,0x7fc7e9c1c0d0,0x7fc7e9c0c5c0,0x7fc7e9c22ef0
  • 1
  • 1

从上述打印地址可以看出:

浅复制只是简单的指针赋值,指向内存仍相同; 
深复制,和完全深复制都实现了内容的复制,但是是否实现对被复制对象的每一层都复制,通过查看多层数组第二层元素的地址来验证:

    NSLog(@"shallow——%p,%p",oldArray[1][0],shallowArray[1][0]);    NSLog(@"oneDeep——%p,%p",oldArray[1][0],oneDeepLevelArray[1][0]);    NSLog(@"trueDeep——%p,%p",oldArray[1][0],trueDeepCopyArray[1][0]);
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

输出结果:

    shallow——0xb000000000000012,0xb000000000000012    oneDeep——0xb000000000000012,0xb000000000000012    trueDeep——0xb000000000000012,0xb000000000000013
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

结论

浅复制地址相同,这点毫无疑问

深复制地址也相同,就说明第二层元素并没有实现内容拷贝,证实ios中的深复制只实现了单层复制

完全复制地址不同,说明归档解档方法实现的完全复制其每一层都实现内容拷贝

上面单层拷贝原文链接