在介绍今天的内容之前我们先看下面的例子
NSArray *array = [NSArray arrayWithObjects:@"1", @"2", nil];
id copyArray = [array copy];
NSLog(@"%@", copyArray);
当执行程序后我们会发现程序的显示如下
(
1,
2
)
从这个例子中我们可以明显的看出,array在通过[array copy]后出现了一个类似于复制的空间,通过打印这个空间对应的值,我们发现其中的内容也与原来的数组相同。
那么就有一个问题产生了,这个复制后的空间是不是原来的空间呢?
于是我们可以通过下面的代码进行测试:
NSLog(@"%p", array);
NSLog(@"%p", copyArray);
通过运行程序可以发现如下显示:
[567:18192] 0x1002042b0
[567:18192] 0x1002042b0
我们可以发现这两个值的地址完全相同,这就告诉了我们其实这两个变量其实指向的是同一个空间。
为什么会这样的问题呢?这就涉及到我们今天介绍的这个OC的关键字copy了
copy
在英文中有复制的意思,所以顾名思义,它的作用就是用来复制某些东西。
通过上面的代码我们可以知道,通过copy得到的空间其实还是原来的空间,造成这样的原因不仅与copy本身的实现有关,也与复制的空间类型密不可分。
copy本身的实现
也许有人通过上面的代码已经猜出copy的实现是通过复制对应空间的指针来实现的,可当你执行下面的代码时又会发现不同的结果:
NSMutableArray *array2 = [NSMutableArray arrayWithObjects:@"1", @"2", nil];
id copyArray2 = [array2 copy];
NSLog(@"%p", array2);
NSLog(@"%p", copyArray2);
<pre name="code" class="objc">NSLog(@"%@", copyArray2);
运行结果如下:
[589:21320] 0x100105c80
[589:21320] 0x100105d60
<pre name="code" class="objc">(
1,
2
)
这就与我们的猜想不符合了,那么我们就会猜想是不是因为数据类型的不同导致实现的结果不同呢?
通过多次的代码运行:
NSString *str1 = @"123";
NSString *str2 = [str1 copy];
NSLog(@"%@", str2);
NSLog(@"%p", str1);
NSLog(@"%p", str2);
结果如下:
[607:22995] 123
[607:22995] 0x1000031e0
[607:22995] 0x1000031e0
我们验证了自己的猜想:
即,当copy的类型为不可变数据类型(NSArray,NSString)时copy复制的是指向该位置的指针,我们将这种现象称之为浅复制,而且无法对对象进行修改。
NSArray *array = [NSArray arrayWithObjects:@"1", @"2", nil];
id copyArray = [array copy];
[copyArray addObject:@"3"];//程序崩溃
相反的,当copy的类型为可变数据类型(NSMutableArray,NSMutableString)时copy复制的时候创造了一个新的空间与指针,相应的这种现象我们称之为深复制,同时无法对对象进行修改。
<pre name="code" class="objc">NSMutableArray *array2 = [NSMutableArray arrayWithObjects:@"1", @"2", nil];
id copyArray2 = [array2 copy];
[copyArray2 addObject:@"3"];//程序崩溃
知道了copy的作用后我们不禁想是不是有一个与其类似的关键字也实现相同的功能,但不再有很大的限制,能对复制的新空间进行修改。
于是我们引入了下面的关键字:
mutableCopy
通过copy我们也大概猜出了mutableCopy的作用,即复制一个空间,那么mutableCopy是否有什么限制呢?
通过运行代码:
NSArray *array = [NSArray arrayWithObjects:@"1", @"2", nil];
id copyArray = [array mutableCopy];
[copyArray addObject:@"3"];//未崩溃
NSLog(@"%@", copyArray);
NSLog(@"%p", array);
NSLog(@"%p", copyArray);
NSMutableString *string = [NSMutableString stringWithFormat:@"123"];
id copyString = [string mutableCopy];
NSLog(@"%@", copyString);
NSLog(@"%p", string);
NSLog(@"%p", copyString);
运行结果:
[617:26216] (
1,
2,
3
)
[617:26216] 0x100102690
[617:26216] 0x1001033e0
[617:26216] 123
[617:26216] 0x100501520
[617:26216] 0x100503750
可以得出结论:mutableCopy深复制,对象地址改变,出现新对象,针对所有对象(可变与不可变为深复制),能对对象进行修改。
那么如果是我们自定义的类中要使用这两个关键字是否有什么限制呢?
自定义一个Person类
@interface Person : NSObject{
@protected
NSString *_name;
}
@property (nonatomic, strong)NSString *name;
@property (nonatomic, assign)NSInteger age;
@property (nonatomic, assign, getter=isLogin)BOOL login;
@property (nonatomic, copy)NSString *coString;
<pre name="code" class="objc">@end
main.m程序如下:
Person *person1 = [[Person alloc] init];
Person *copyPerson = [person1 mutableCopy];
copyPerson.name = @"Flack";
当运行后我们发现程序崩溃了,显示的错误如下:
reason: '-[Person mutableCopyWithZone:]: unrecognized selector sent to instance
它告诉我们有一个方法mutableCopyWithZone:没有实现
但是当我们对Person进行重写时会发现没有这个函数,造成这样的原因是什么呢?
通过对比能实现其他能实现的类,我们会发现它们的类中都会有这样的一句:
<NSCopying,NSMutableCopying>
于是我们尝试将其加入类中:
@interface Person : NSObject<NSCopying,NSMutableCopying>
{
@protected
NSString *_name;
}
@property (nonatomic, strong)NSString *name;
@property (nonatomic, assign)NSInteger age;
@property (nonatomic, assign, getter=isLogin)BOOL login;
@property (nonatomic, copy)NSString *coString;
<pre name="code" class="objc">@end
Person中将方法重写
- (id)mutableCopyWithZone:(NSZone *)zone{
Person *copyPerson = [[[self class] allocWithZone:zone] init];
copyPerson.name = self.name;
copyPerson.age = self.age;
copyPerson.login = self.isLogin;
copyPerson.coString = self.coString;
return copyPerson;
}
- (id)copyWithZone:(NSZone *)zone{
return self;
}
再次运行如下程序:
Person *person1 = [[Person alloc] init];
Person *copyPerson = [person1 mutableCopy];
copyPerson.name = @"Flack";
NSLog(@"%@", copyPerson.name);
NSLog(@"%p", person1);
NSLog(@"%p", copyPerson);
这次终于得到了结果:
[892:44560] Flack
[892:44560] 0x1006001c0
[892:44560] 0x1006002d0
所以,总结如下:
使用copy或mutableCopy必须为遵守过协议<NSCopying, NSMutableCopying>的对象才能使用,而且要将
- (id)copyWithZone:(nullable NSZone *)zone;
- (id)mutableCopyWithZone:(nullable NSZone *)zone;
重写实现