关于 strong,copy,深拷贝,浅拷贝 的整理

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];
}

即通过一个一个对元素进行深拷贝,重新添加到可变数组中

 

参考文章:

iOS 浅谈:深.浅拷贝与copy.strong

NSString到底是用copy还是strong?

iOS 集合的深复制与浅复制

iOS 深拷贝两种实现

OC数组中的深拷贝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值