copy与mutableCopy方法
copy方法用于复制对象的副本。通常来说,copy方法总是返回对象的不可修改的副本,即使该对象本身是可修改的。例如,程序调用NSMutableString的copy方法,将会返回不可修改的字符串对象。
mutableCopy方法用于复制对象的可变副本。通常来说,mutableCopy方法总是返回该对象可修改的副本,即使被复制的对象本身是不可修改的,调用mutableCopy方法复制出来的副本也是可修改的。例如,程序调用NSString的mutableCopy方法,将会返回一个NSMutableString对象。
我们先来看下面代码:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableString * str = [NSMutableString stringWithString:@"123"];
//先创建一个可变字符串
NSMutableString * strcopy = [str mutableCopy];
//复制字符串的可变副本
[strcopy appendString:@"456"];
NSLog(@"%@",strcopy);
NSLog(@"%@",str);
}
return 0;
}
最终我们发现原字符串并没有发生改变,字符串副本发生了改变。
然后我们在原代码后面加上两行,创造一个不可改变的副本改变它的值试试看。
NSMutableString * strcopy2 = [str copy];
[strcopy2 appendString:@"789"];
运行会报错
由此可以看出对副本的改变,对原始对象不会有任何影响,并且用copy方法形成的副本是不可改变的。
NSCopying与NSMutableCopy协议
使用copy和mutablecopy复制对象的副本是否可以用于自定义的类呢?
我们自己定义一个类试试
// FKPerson.h
#import <Foundation/Foundation.h>
@interface FKPerson : NSObject
@property (nonatomic,strong) NSMutableString * name;
@property (nonatomic,assign) int age;
@end
// FKPerson.m
#import "FKPerson.h"
@implementation FKPerson
@synthesize name;
@synthesize age;
@end
//main.m
#import <Foundation/Foundation.h>
#import "FKPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
FKPerson * zhai = [[FKPerson alloc] init];
zhai.name = [NSMutableString stringWithString:@"666"];
zhai.age = 19;
FKPerson * zhai2 = [zhai copy];
}
return 0;
}
结果显示会报错:
报错的原因是找不到mutableCopyWithZone:方法
为了解决这个问题,要做如下事情:
🎇让该类实现NSCopying / NSMutableCopying协议
🎇让该类实现copyWithZone / mutableCopyWithZone方法
当使用调用对象的copy方法来复制自身时,程序底层需要调用copyWithZone:方法来完成实际的复制工作,从朋友返回的实际上就是copyWithZone:方法的返回值;当程序调用对象的mutableCopy方法来复制自身时,程序底层需要调用mutableCopyWithZone:方法来完成实际的复制工作,mutableCopy返回的实际上就是mutableCopyWithZone:方法的返回值。
为了保证FKPerson类可调用copy方法来复制自身,程序可先在FKPerson类的接口上实现NSCopying协议,然后在FKPerson类的实现部分增加如下的copyWithZone:方法。
// FKPerson.h
#import <Foundation/Foundation.h>
@interface FKPerson : NSObject <NSCopying>
@property (nonatomic,strong) NSMutableString * name;
@property (nonatomic,assign) int age;
@end
// FKPerson.m
#import "FKPerson.h"
@implementation FKPerson
@synthesize name;
@synthesize age;
- (id)copyWithZone:(NSZone *)zone {
NSLog(@"执行copyWithZone方法");
FKPerson * a = [[[self class]allocWithZone:zone]init];
a.name = self.name;
a.age = self.age;
return a;
}
@end
//main.m
#import <Foundation/Foundation.h>
#import "FKPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
FKPerson * zhai = [[FKPerson alloc] init];
zhai.name = [NSMutableString stringWithString:@"666"];
zhai.age = 19;
FKPerson * zhai2 = [zhai copy];
zhai2.name = [NSMutableString stringWithString:@"999"];
zhai2.age = 20;
NSLog(@"%@",zhai.name);
NSLog(@"%d",zhai.age);
NSLog(@"%@",zhai2.name);
NSLog(@"%d",zhai2.age);
}
return 0;
}
输出结果:
setter方法的复制选项
在之前合成setter和getter方法的时候可以用到copy指示符,copy指示符指定程序调用setter方法复制时,实际上是将传入参数的副本赋值给程序的实例变量。
还是刚才那个FKPerson类,我们对它的接口和实现进行修改:
// FKPerson.h
#import <Foundation/Foundation.h>
@interface FKPerson : NSObject <NSCopying>
@property (nonatomic,copy) NSMutableString * name;
@end
// FKPerson.m
#import "FKPerson.h"
@implementation FKPerson
@synthesize name;
@end
#import <Foundation/Foundation.h>
#import "FKPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
FKPerson * zhai = [[FKPerson alloc] init];
zhai.name = [NSMutableString stringWithString:@"666"];
[zhai.name appendString:@"999"];
}
return 0;
}
程序在编译时没有问题,在运行时报错:
这段错误提示name属性不可修改,这是因为程序定义name属性时使用了copy指示符,该指示符指定调用setName:方法,程序实际上会使用参数的副本对name实例变量赋值。
copy方法默认是复制该对象的不可变副本,虽然程序传入的NSMutableString,但程序调用该参数的copy方法得到的是不可变副本。因此,程序赋给FKPerson对象的name实例变量的值依然是不可变字符串。