copy
这个英文单词,让我第一个想起的是copy忍者卡卡西。我的很是喜欢卡卡西,和谁对战都是五五开的上忍。copy
翻译成中文就是复制的意思,为何咱们想要复制呢?我以为缘由有下面几点:面试
那么回到iOS开发,其实类比到生活中也差很少。修改复制出来的东西,不但愿影响原来的内容。objective-c
针对copy
就会引出一些面试题:数组
NSString
属性,可使用strong
关键字修饰吗?若是能够,何时使用strong
修饰,何时使用copy
修饰?NSMutableArray
属性,关键字使用copy
,像NSMutableArray
中添加元素会发生什么现象?NSString
NSMutableString
NSArray
NSMutableArray
NSDictionary
NSMutableDictionary
调用copy
方法或者mutableCopy
方法,是深拷贝仍是浅拷贝?等等...app
下面咱们就来探究一下copy
atom
NSString
这个类表明不可变字符串。不可变意味着建立出来的字符串对象不能够被修改str1
指向字符串对象str1
调用copy
方法,str2
指向copy
出来的对象str1
调用mutableCopy
方法,str3
指向mutableCopy
出来的对象- (void)viewDidLoad {
[super viewDidLoad];
NSString *str1 = @"test";
NSString *str2 = [str1 copy];
NSMutableString *str3 = [str1 mutableCopy];
NSLog(@"\n str1 -> %@ \n str2 -> %@ \n str3 -> %@", str1, str2, str3);
NSLog(@"\n str1 -> %p \n str2 -> %p \n str3 -> %p", str1, str2, str3);
}
// 打印结果:
2020-06-04 22:47:42.843279+0800 05_copy[34618:3042446]
str1 -> test
str2 -> test
str3 -> test
2020-06-04 22:47:42.843496+0800 05_copy[34618:3042446]
str1 -> 0x10c9a0020
str2 -> 0x10c9a0020
str3 -> 0x600001836fa0
复制代码
从打印结果能够看出:spa
copy
仍是mutaleCopy
方法都成功复制了”test“这个文本str1
和str2
都指向了同一个对象,str3
指向了另一个对象。画图分析一波:翻译
str1
和st2
都指向了同一个对象,str3
指向了另一个对象。copy
方法会返回一个不可变对象,而调用mutableCopy
方法会返回一个可变对象。copy
执行完毕以后,彻底能够指向以前的对象,反正没办法进行修改,这样反而节省了内存空间。NSString
建立的对象,调用copy
方法不会建立新的对象,只是指针的拷贝,属于浅拷贝。而调用mutableCopy
方法会建立一个与以前内容同样的新的对象,属于深拷贝。str1
指向该对象str1
调用copy
方法,使用str2
指向返回的对象str1
调用mutableCopy
方法,使用str3
指向返回的对象- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *str1 = [[NSMutableString alloc] initWithString:@"test"];
NSString *str2 = [str1 copy];
NSMutableString *str3 = [str1 mutableCopy];
NSLog(@"\n str1 -> %@ \n str2 -> %@ \n str3 -> %@", str1, str2, str3);
NSLog(@"\n str1 -> %p \n str2 -> %p \n str3 -> %p", str1, str2, str3);
}
// 打印结果:
2020-06-05 12:16:42.174359+0800 05_copy[35192:3110215]
str1 -> test
str2 -> test
str3 -> test
2020-06-05 12:16:42.174536+0800 05_copy[35192:3110215]
str1 -> 0x600003d282d0
str2 -> 0xbf8fdf3650605176
str3 -> 0x600003d28270
复制代码
结果分析:3d
st1
和str2
和str3
的内容都是”test“,成功复制画图分析:指针
st1
是指向的是可变字符串,能够进行修改str1调用
copy方法会从新建立一个新的不可变字符串,是深拷贝,当
str1进行修改的时候,
str2`中的值不会受到任何的影响str1
调用mutableCopy
方法会建立一个新的能够变字符串,那么就能够对这个可变字符串进行修改,不影响其str1
和str2
,是深拷贝。而且三者互不影响。NSString
相似,就再也不说明了,直接看代码- (void)viewDidLoad {
[super viewDidLoad];
NSArray *array1 = @[@"a", @"b", @"c"];
NSArray *array2 = [array1 copy];
NSMutableArray *array3 = [array1 mutableCopy];
NSLog(@"\n array1 -> %@ \n array2 -> %@ \n array3 -> %@", array1, array2, array3);
NSLog(@"\n array1 -> %p \n array2 -> %p \n array3 -> %p", array1, array2, array3);
}
// 打印结果:
2020-06-05 12:32:17.303200+0800 05_copy[35231:3117262]
array1 -> (
a,
b,
c
)
array2 -> (
a,
b,
c
)
array3 -> (
a,
b,
c
)
2020-06-05 12:32:17.303393+0800 05_copy[35231:3117262]
array1 -> 0x600003a0c360
array2 -> 0x600003a0c360
array3 -> 0x600003a0c0f0
复制代码
打印结果分析:code
array1
array2
和 array3
存储的地址值来看,调用copy
方法进行了浅拷贝,而调用mutableCopy
方法是深拷贝。也就是说,修改array4
里面的值不会影响array1
和 array2
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *array1 = [[NSMutableArray alloc] initWithObjects:@"a", @"b", @"c", nil];
NSArray *array2 = [array1 copy];
NSMutableArray *array3 = [array1 mutableCopy];
NSLog(@"\n array1 -> %@ \n array2 -> %@ \n array3 -> %@", array1, array2, array3);
NSLog(@"\n array1 -> %p \n array2 -> %p \n array3 -> %p", array1, array2, array3);
}
// 打印结果:
2020-06-05 12:37:57.830060+0800 05_copy[35251:3120031]
array1 -> (
a,
b,
c
)
array2 -> (
a,
b,
c
)
array3 -> (
a,
b,
c
)
2020-06-05 12:37:57.830295+0800 05_copy[35251:3120031]
array1 -> 0x60000378d6e0
array2 -> 0x60000378d950
array3 -> 0x60000378d9b0
复制代码
结果分析:
copy
仍是 mutable
方法都是深拷贝总结:
在OC中,其实还有不少相似的类好比NSDictionary
NSMutableDictionary
NSSet
NSMutableSet
结论都是同样的。能够本身去敲一段代码来验证。经常使用的我总结以下表:
copy | mutableCopy | |
---|---|---|
NSString | 返回NSString、浅拷贝 | 返回NSMutableString、深拷贝 |
NSMutableString | 返回NSString、深拷贝 | 返回NSMutableString、深拷贝 |
NSArray | 返回NSArray、浅拷贝 | 返回NSMutableArray、深拷贝 |
NSMutableArray | 返回NSArray、深拷贝 | 返回NSMutableArray、深拷贝 |
NSDictionary | 返回NSDictionary、浅拷贝 | 返回NSMutableDictionary、深拷贝 |
NSMutableDictionary | 返回NSDictionary、深拷贝 | 返回NSMutableDictionary、深拷贝 |
上面已经讲清楚了,copy
和 mutableCopy
针对于不一样的类返回结果以及是否产生新的对象作了分析和总结。
还遗留了点问题
NSString
属性的时候,NSString
一般定义为copy
定义成strong
行不行?若是二者都行,开发中该使用哪个?NSMutableArray
NSMutableString
NSMutableDictionary
的属性,能不能用copy
,会不会有什么问题?copy
修改为 strong
,那么打印结果又是什么呢?@interface ViewController ()
@property (nonatomic, copy) NSString *str;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *mStr = [[NSMutableString alloc] initWithString:@"test"];
self.str = mStr;
[mStr appendString:@"haha"];
NSLog(@"mStr -> %@", mStr);
NSLog(@"self.str -> %@", self.str);
}
复制代码
copy
关键字修饰的打印结果:mstr -> testhaha
self.str -> test
复制代码
strong
关键字修饰的打印结果:mstr -> testhaha
self.str -> testhaha
复制代码
这个问题的本质在于这句代码,这句代码的本质呢是在调用str的setter方法,最关键的地方就是要知道setter方法的内部是怎么写的呢?
self.str = mStr;
复制代码
- (void)setStr:(NSString *)str {
if (_str != str) {
[_str release];
_str = [str copy]; // 最关键的地方
}
}
复制代码
copy
方法,赋值给成员变量。str
就是mstr
,将一个可变字符串进行copy
后会建立一个新的对象,_str
指向了一个新的对象。因此你再去修改曾经的mStr
的值不会影响_str
的值。若是定义字符串属性的时候,使用strong
关键字呢?仍是从本质出发,setter方法里的这句代码变了。变成retain
了。因此_str
指向原来的位置,当你修改mStr的值时候,_str确定会跟着改变。
_str = [str retain]; // 最关键的地方
复制代码
若是你还不懂,再画个图给你解释:
@interface ViewController ()
@property (nonatomic, copy) NSMutableArray *mArray;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.mArray = [[NSMutableArray alloc] initWithObjects:@"a", @"b", @"c", nil];
[self.mArray addObject:@"d"];
NSLog(@"%@", self.mArray);
}
复制代码
-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x600002704060
复制代码
copy
修饰,会建立一个新对象,而新对象是不可变数组,不可变数组调用addObject
方法怎么可能找获得呢?self.mArray = [[NSMutableArray alloc] initWithObjects:@"a", @"b", @"c", nil];
复制代码
因此最终结果必定是崩溃。