1. copy返回immutable对象;所以,如果对copy返回值使用mutable对象接口就会crash;
mutableCopy返回mutable对象;
2、自定义对象的复制
使用copy和mutableCopy复制对象的副本使用起来确实方便,那么我们自定义的类是否可调用copy与mutableCopy方法来复制副本呢?先定义一个Person类,代码如下:
@interfacePerson:NSObject
@property(nonatomic,assign)NSIntegerage;
@property(nonatomic,copy)NSString *name;
@end
然后尝试调用Person的copy方法来复制一个副本:
Person *person1=[[Personalloc]init];//创建一个Person对象
person1.age=20;
person1.name=@"张三";
Person *person2=[person1copy];//复制副本
运行程序,将会发生崩溃,并输出以下错误信息:
[PersoncopyWithZone:]:unrecognized selector senttoinstance0x608000030920
上面的提示:Person找不到copyWithZone:方法。我们将复制副本的代码换成如下:
Person *person2=[person1mutableCopy];//复制副本
再次运行程序,程序同样崩溃了,并输出去以下错误信息:
[PersonmutableCopyWithZone:]:unrecognized selector senttoinstance0x600000221120
上面的提示:Person找不到mutableCopyWithZone:方法。
大家可能会觉得疑惑,程序只是调用了copy和mutableCopy方法,为什么会提示找不到copyWithZone:与mutableCopyWithZone:方法呢?其实当程序调用对象的copy方法来复制自身时,底层需要调用copyWithZone:方法来完成实际的复制工作,copy返回实际上就是copyWithZone:方法的返回值;mutableCopy与mutableCopyWithZone:方法也是同样的道理。
那么怎么做才能让自定义的对象进行copy与mutableCopy呢?需要做以下事情:
1.让类实现NSCopying/NSMutableCopying协议。
2.让类实现copyWithZone:/mutableCopyWithZone:方法
所以让我们的Person类能够复制自身,我们需要让Person实现NSCopying协议;然后实现copyWithZone:方法:
@interfacePerson:NSObject
@property(nonatomic,assign)NSIntegerage;
@property(nonatomic,copy)NSString *name;
@end
#import "Person.h"
@implementationPerson
-(id)copyWithZone:(NSZone *)zone{
Person *person=[[[selfclass]allocWithZone:zone]init];
person.age=self.age;
person.name=self.name;
returnperson;
}
@end
运行之后发现我们实现了对象的复制:
同时需要注意的是如果对象中有其他指针类型的实例变量,且只是简单的赋值操作:person.obj2 = self.obj2,其中obj2是另一个自定义类,如果我们修改obj2中的属性,我们会发现复制后的person对象中obj2对象中的属性值也变了,因为对于这个对象并没有进行copy操作,这样的复制操作不是完全的复制,如果要实现完全的复制,需要将obj2对应的类也要实现copy,然后这样赋值:person.obj2 = [self.obj2 copy]。如果对象很多或者层级很多,实现起来还是很麻烦的。如果需要实现完全复制同样还有另有一种方法,那就是归档:
Person *person2=[NSKeyedUnarchiverunarchiveObjectWithData:[NSKeyedArchiverarchivedDataWithRootObject:person1]];
这样我们就实现了自定义对象的复制,需要指出的是如果重写copyWithZone:方法时,其父类已经实现NSCopying协议,并重写过了copyWithZone:方法,那么子类重写copyWithZone:方法应先调用父类的copy方法复制从父类继承得到的成员变量,然后对子类中定义的成员变量进行赋值:
-(id)copyWithZone:(NSZone *)zone{
idobj=[supercopyWithZone:zone];
//对子类定义的成员变量赋值
...
returnobj;
}
关于mutableCopy的实现与copy的实现类似,只是实现的是NSMutableCopying协议与mutableCopyWithZone:方法。对于自定义的对象,在我看来并没有什么可变不可变的概念,因此实现mutableCopy其实是没有什么意义的,在此就不详细介绍了。
3、定义属性的copy指示符
如下段代码,我们在定义属性的时候使用了copy指示符:
#import
@interfacePerson:NSObject
@property(nonatomic,copy)NSMutableString *name;
@end
使用如下代码来进行测试:
Person *person1=[[Personalloc]init];//创建一个Person对象
person1.name=[NSMutableStringstringWithString:@"苏小妖"];
[person1.nameappendString:@"123"];
运行程序会崩溃,并且提示以下信息:
***Terminating app duetouncaughtexception'NSInvalidArgumentException',reason:'Attempt to mutate immutable object with appendString:'
这段错误提示不允许修改person的name属性,这是因为程序定义name属性时使用了copy指示符,该指示符置顶调用setName:方法时(通过点语法赋值时,实际上是调用对应的setter方法),程序实际上会使用参数的副本对name实际变量复制。也就是说,setName:方法的代码如下:
-(void)setName:(NSMutableString *)name{
_name=[namecopy];
}
copy方法默认是复制该对象的不可变副本,虽然程序传入的NSMutableString,但程序调用该参数的copy方法得到的是不可变副本。因此,程序赋给Person对象的name实例变量的值依然是不可变字符串。
注意:定义合成getter、setter方法时并没有提供mutableCopy指示符。因此即使定义实例变量时使用了可变类型,但只要使用copy指示符,实例变量实际得到的值总是不可变对象。
copy与mutableCopy还有一个特点:
修改源对象的属性和行为,不会影响副本对象
修改副本对象的属性和行为,不会影响源对象
互不影响