I. strong,copy
先上结论:
String:推荐采用 copy 或 strong 进行修饰。对于来源于不可变的字符串,两者无影响(copy需执行一次判断,会些许影响性能);对于来源于可变的字符串,需采用 copy 进行修饰
MutableArray:需采用 strong 进行修饰。若采用copy,对赋值对象进行深copy,mutableArray等同于Array,再进行mutableArray的方法会造成崩溃
Array:需采用 copy 进行修饰。若采用strong,副本对象会改变原对象的值
Dictionary的修饰同Array
a.关于NSString
首先声明两个属性变量
@property (nonatomic, strong) NSString *strongStr;
@property (nonatomic, copy) NSString *cpyStr;
1-对于来源于不可变的字符串进行测试
NSString *strong1 = @"I am Strong!";
NSString *copy1 = @"I am Copy!";
self.strongStr = strong1;
self.cpyStr = copy1;
NSLog(@"strong1 = %p",strong1);
NSLog(@"stringStrong = %p",self.strongStr);
NSLog(@"copy1 = %p",copy1);
NSLog(@"stringCopy = %p",self.cpyStr);
打印结果:
strong1 = 0x102f1e2c0
_strongStr = 0x102f1e2c0
copy1 = 0x102f1e2e0
_cpyStr = 0x102f1e2e0
结论:即对于来源于不可变的字符串,用 strong 进行修饰与 copy 进行修饰,都进行了浅Copy
2-对于来源于可变的字符串进行测试
NSMutableString *mutableStrong = [NSMutableString stringWithString:@"StrongMutable"];
NSMutableString *mutableCopy = [NSMutableString stringWithString:@"CopyMutable"];
self.strongStr = mutableStrong;
self.cpyStr = mutableCopy;
[mutableStrong appendString:@"--appendStrong!"];
[mutableCopy appendString:@"--appendCopy!"];
NSLog(@"%p mutableStrong = %@",mutableStrong, mutableStrong);
NSLog(@"%p _strongStr = %@",self.strongStr, self.strongStr);
NSLog(@"%p mutableCopy = %@",mutableCopy, mutableCopy);
NSLog(@"%p _cpyStr = %@",self.cpyStr, self.cpyStr);
打印结果:
[32269:596739] 0x600000045a30 mutableStrong = StrongMutable--appendStrong!
[32269:596739] 0x600000045a30 _strongStr = StrongMutable--appendStrong!
[32269:596739] 0x60000024dc80 mutableCopy = CopyMutable--appendCopy!
[32269:596739] 0x60000003ffe0 _cpyStr = CopyMutable
结论:对于来源于可变的字符串,用strong修饰的字符串依旧进行了浅Copy,原对象与赋值对象共用一处内存,修改其中一个会影响另一个;而由copy修饰的字符串进行了深Copy,原对象与赋值对象互相独立
Q:既然copy安全,那为什么不都用copy?
A:copy修饰的NSString在进行set操作时,底层是这样实现的:
进行 str = sourceStr 操作时,内部会执行一个操作:str = [sourceStr copy];
那么这个copy里面做了什么呢?
if ([str isMemberOfClass:[str class]])
即进行一次判断,判断来源是可变的还是不可变的。如果是不可变,接下来的操作就跟strong修饰的没有区别,进行浅拷贝;如果是可变的,那么会进行一次深拷贝
所以,当你的项目十分庞大时,copy操作内部进行的判断多多少少会对你的app的性能造成一定的影响。
b.关于NSArray
被strong修饰之后,由于只是强引用,所以副本对象数组和源对象数组只是指向同一个内存区域。这样就会造成副本对象数组会随着源对象数组的改变而改变,即便有时候你并不想让副本对象跟着改变。
被copy修饰之后,源对象数组被copy了一份,源对象数组和副本对象数组是不同的。所以副本对象数组并不会随着源对象数组改变。
c.关于NSMutableArray
@property (nonatomic, copy) NSMutableArray *mArray;//等同于
- (void)setMArray:(NSMutableArray *)mArray {
_mArray = mArray.copy;
}
当执行 copy 操作以后,mArray属性就成了NSArray。NSMutableArray只能用strong修饰,不存在有copy修饰的情况,写了就成NSArray了。
如果是strong,直接是赋值_mArray = mArray;右边是什么,左边就是什么,并且是强引用新值。这样就和strong的作用一样了。
所以:用copy为关键字的话,调用setter方法后,是对赋值对象进行深拷贝。并且拷贝的对象是copy的(不可变的),而不是mutableCopy的(可变的)。所以用copy修饰的mutableArray也被视为Array了,所以再用mutableArray的方法就会发生崩溃。
II. 深拷贝 & 浅拷贝
浅拷贝
浅拷贝,指仅将对象内存地址多了一个引用。即拷贝结束之后,两个对象不仅值相同,而且对象所指的内存地址也是一样的。
单层深拷贝
对于不可变的容器类对象(如NSArray、NSSet、NSDictionary)进 mutableCopy 操作,内存地址发生了变化,但是其中的元素内存地址并没有发生变化,属于单层深拷贝。
对于可变集合类对象(如NSMutableArray、NSMutableSet、NSMutableDictionary),不管是进行 copy 操作还是 mutableCopy 操作,其内存地址都发生了变化,但是其中的元素内存地址都没有发生变化,属于单层深拷贝。
深拷贝
深拷贝,指拷贝一个对象的具体内容。拷贝结束之后,两个对象的值相同,但是指向的内存地址不同。两个对象之间也互不影响。
a.非集合类对象的 copy 和 mutableCopy
1-以NSString为例
NSString *string = @"Alonso";
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
NSLog(@"string: %p, %p", string, &string);
NSLog(@"stringCopy: %p, %p", stringCopy, &stringCopy);
NSLog(@"stringMCopy: %p, %p", stringMCopy, &stringMCopy);
打印结果:
string: 0x107ea22c0, 0x7ffee7d5f8f8
stringCopy: 0x107ea22c0, 0x7ffee7d5f8f0
stringMCopy: 0x60400025cec0, 0x7ffee7d5f8e8
2-以NSMutableString为例
NSMutableString *string = [NSMutableString stringWithFormat:@"Hamilton"];
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
NSLog(@"string: %p, %p", string, &string);
NSLog(@"stringCopy: %p, %p", stringCopy, &stringCopy);
NSLog(@"stringMCopy: %p, %p", stringMCopy, &stringMCopy);
打印结果:
string: 0x604000441050, 0x7ffee63478f8
stringCopy: 0xa00f481810840cb8, 0x7ffee63478f0
stringMCopy: 0x604000440fc0, 0x7ffee63478e8
结论:在非集合类对象中,对不可变对象进行 copy 操作,只仅仅是指针复制,进行 mutableCopy 操作,是内容复制。
对可变对象进行 copy 和 mutableCopy 操作,都是内容复制。
b.集合类对象的 copy 和 mutableCopy
1-以NSArray为例
NSString *element_01 = @"Terpstra";
NSString *element_02 = @"Stybar";
NSString *element_03 = @"Lampaert";
NSArray *array = @[element_01, element_02, element_03];
NSArray *arrayCopy = [array copy];
NSMutableArray *arrayMCopy = [array mutableCopy];
NSLog(@"array: %p, %p; array.firstObject: %p", array, &array, array.firstObject);
NSLog(@"arrayCopy: %p, %p; arrayCopy.firstObject: %p", arrayCopy, &arrayCopy, arrayCopy.firstObject);
NSLog(@"arrayMCopy: %p, %p; arrayMCopy.firstObject: %p", arrayMCopy, &arrayMCopy, arrayMCopy.firstObject);
打印结果:
array: 0x6000002599e0, 0x7ffee7cfe8d0; array.firstObject: 0x107f032c0
arrayCopy: 0x6000002599e0, 0x7ffee7cfe8c8; arrayCopy.firstObject: 0x107f032c0
arrayMCopy: 0x600000259980, 0x7ffee7cfe8c0; arrayMCopy.firstObject: 0x107f032c0
2-以NSMutableArray为例
NSString *element_01 = @"Terpstra";
NSString *element_02 = @"Degenkolb";
NSString *element_03 = @"Hayman";
NSMutableArray *array = [NSMutableArray arrayWithArray:@[element_01, element_02, element_03]];
NSArray *arrayCopy = [array copy];
NSMutableArray *arrayMCopy = [array mutableCopy];
NSLog(@"array: %p, %p; array.firstObject: %p", array, &array, array.firstObject);
NSLog(@"arrayCopy: %p, %p; arrayCopy.firstObject: %p", arrayCopy, &arrayCopy, arrayCopy.firstObject);
NSLog(@"arrayMCopy: %p, %p; arrayMCopy.firstObject: %p", arrayMCopy, &arrayMCopy, arrayMCopy.firstObject);
打印结果:
array: 0x604000247b90, 0x7ffee301e8d0; array.firstObject: 0x10cbe32c0
arrayCopy: 0x604000247b30, 0x7ffee301e8c8; arrayCopy.firstObject: 0x10cbe32c0
arrayMCopy: 0x604000247f20, 0x7ffee301e8c0; arrayMCopy.firstObject: 0x10cbe32c0
结论:
对于不可变的集合类对象进行 copy 操作,只是改变了指针,其内存地址并没有发生变化;进行 mutableCopy 操作,内存地址发生了变化,但是其中的元素内存地址并没有发生变化。
对于可变集合类对象,不管是进行 copy 操作还是 mutableCopy 操作,其内存地址都发生了变化,但是其中的元素内存地址都没有发生变化,属于单层深拷贝。
c.如何解决(期望深拷贝,实际单层深拷贝)
以数组为例:
首先,自己定义的类对象一般需要遵循 NSCopying, NSMutableCopying 协议,例如
// MXBdlElementModel.h
@interface MXBdlElementModel : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic, strong) NSString *bundleID;
@end
// MXBdlElementModel.m
@implementation MXBdlElementModel
- (id)copyWithZone:(NSZone *)zone {
MXBdlElementModel *bModel = [[self class] allocWithZone:zone];
bModel.bundleID = self.bundleID;
return bModel;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
MXBdlElementModel *bModel = [[self class] allocWithZone:zone];
bModel.bundleID = self.bundleID;
return bModel;
}
@end
Main.m
// 深拷贝数组
_originArr = [NSMutableArray arrayWithCapacity:_bundleArr.count];
for (int i = 0; i < _bundleArr.count; i++) {
MXBdlElementModel *newMdl = [_bundleArr[i] copy];
[_originArr addObject:newMdl];
}
即通过一个一个对元素进行深拷贝,重新添加到可变数组中
参考文章: