iOS开发中的种种...将来跳槽用

strong、copy和mutableCopy详解

2016-06-02  本文已影响689人  KFAaron

写在前面

关于copymutableCopystrong,一直想好好整理整理,但是因为各种原因搁置了。今天上班时发现老代码中因为这个问题出现了一个特别恶心的大坑,让我下定决心写下这篇博文。如果你认为自己没掌握copy相关知识,建议看看此文,这里会有你需要的;如果你认为自己掌握了copy相关知识,也建议看看此文,也许这里有你感兴趣的东西。

首先和大家回顾一下有关copy的两个概念——浅拷贝(Shallow Copy)深拷贝(Deep Copy)

浅拷贝:只拷贝指针,不拷贝内容(不用为内容分配新的内存空间);
深拷贝:同时拷贝指针和内容,即分配一块新的内存,将原内容复制一份到该内存中,并生成指向该内存的新指针。(这里谢谢@灿烂天空的提醒,原来的解释可能不太准确,特此修正)
通俗点解释就是,已知一条路,通往罗马,浅拷贝就是新建一条通往这个罗马的路。而深拷贝则是选一个地方,按照原来的罗马新建一个(罗马内部包含的所有东西全部照旧的新建,比如家居、文化等等),并修一条通往新罗马的路,这样原来罗马怎么样就跟新罗马没关系了。

In the case of collection objects, a shallow copy means that a new collection object is created, but the contents of the original collection are not duplicated—only the object references are copied to the new container.

A deep copy duplicates the compound object as well as the contents of all of its contained objects.

四种copy情况

不可变非集合类对象的copy

直接上代码:

NSString *immutableStr = @"KFA_test1";
NSString *copyImmutableStr = [immutableStr copy];
NSString *mutableCopyImmutableStr = [immutableStr mutableCopy];
NSLog(@"\\nimmutableStr:%p-%p\\ncopyImmutableStr:%p-%p\\nmutableImmutableStr:%p-%p",immutableStr,&immutableStr,copyImmutableStr,&copyImmutableStr,mutableCopyImmutableStr,&mutableCopyImmutableStr);

运行结果

immutableStr:0x103426098-0x7fff5c7dba08
copyImmutableStr:0x103426098-0x7fff5c7dba00
mutableImmutableStr:0x7ffd99d34ce0-0x7fff5c7db9f8

可以发现,对不可变非集合类对象进行copy时,只拷贝了指针,内容不拷贝。对不可变非集合类对象进行mutableCopy时,同时拷贝了指针和内容。

可变非集合类对象的copy

照例上代码:

NSMutableString *mutableStr = [@"KFA_test2" mutableCopy];
NSString *copyImmutableStr = [mutableStr copy];
NSString *mutableCopyImmutableStr = [mutableStr mutableCopy];
NSLog(@"\\nmutableStr:%p-%p\\ncopyImmutableStr:%p-%p\\nmutableImmutableStr:%p-%p",mutableStr,&mutableStr,copyImmutableStr,&copyImmutableStr,mutableCopyImmutableStr,&mutableCopyImmutableStr);

运行结果:

mutableStr:0x7feb8bc1c940-0x7fff5610fa08
copyImmutableStr:0xa34cf2e0400c1289-0x7fff5610fa00
mutableImmutableStr:0x7feb8bc1f320-0x7fff5610f9f8

从结果可以发现,对可变非集合类对象进行copymutableCopy,都同时拷贝指针和内容。

不可变集合类对象的copy

这里以数组为例:

NSArray *immutableArr = @[@"KFA_test3"];
NSString *immu = immutableArr.firstObject;
NSArray *copyImmutableArr = [immutableArr copy];
NSString *copyImmu = copyImmutableArr.firstObject;
NSArray *mutableCopyImmutableArr = [immutableArr mutableCopy];
NSString *mutableCopyImu = mutableCopyImmutableArr.firstObject;
NSLog(@"\\nimmutableArr:%p-%p\\ncopyImmutableArr:%p-%p\\nmutableCopyImmutableArr:%p-%p\\nimmu:%p-%p\\ncopyImmu:%p-%p\\nmutableCopyImu:%p-%p",immutableArr,&immutableArr,copyImmutableArr,&copyImmutableArr,mutableCopyImmutableArr,&mutableCopyImmutableArr,immu,&immu,copyImmu,&copyImmu,mutableCopyImu,&mutableCopyImu);

运行结果:

immutableArr:0x7fa44a7087d0-0x7fff576619d0
copyImmutableArr:0x7fa44a7087d0-0x7fff576619c0
mutableCopyImmutableArr:0x7fa44a703370-0x7fff576619b0
immu:0x1085a0118-0x7fff576619c8
copyImmu:0x1085a0118-0x7fff576619b8
mutableCopyImu:0x1085a0118-0x7fff576619a8

不难发现,对不可变集合类对象进行copy时,只拷贝指针,不拷贝内容。对不可变集合类对象进行mutableCopy时,同时拷贝指针和内容,但是这里的内容只针对集合对象本身,对于集合内的元素只拷贝指针,不拷贝内容。

可变集合类对象的copy

还是以数组为例:

NSArray *mutableArr = [@[@"KFA_test3"] mutableCopy];
NSString *mu = mutableArr.firstObject;
NSArray *copyImmutableArr = [mutableArr copy];
NSString *copyImmu = copyImmutableArr.firstObject;
NSArray *mutableCopyImmutableArr = [mutableArr mutableCopy];
NSString *mutableCopyImu = mutableCopyImmutableArr.firstObject;
NSLog(@"\\nmutableArr:%p-%p\\ncopyImmutableArr:%p-%p\\nmutableCopyImmutableArr:%p-%p\\nmu:%p-%p\\ncopyImmu:%p-%p\\nmutableCopyImu:%p-%p",mutableArr,&mutableArr,copyImmutableArr,&copyImmutableArr,mutableCopyImmutableArr,&mutableCopyImmutableArr,mu,&mu,copyImmu,&copyImmu,mutableCopyImu,&mutableCopyImu);

运行结果:

mutableArr:0x7feacbd0d250-0x7fff521409d0
copyImmutableArr:0x7feacbd06ef0-0x7fff521409c0
mutableCopyImmutableArr:0x7feacbd0c2a0-0x7fff521409b0
mu:0x10dac1118-0x7fff521409c8
copyImmu:0x10dac1118-0x7fff521409b8
mutableCopyImu:0x10dac1118-0x7fff521409a8

我们发现,对可变集合类对象进行copymutableCopy时,都同时拷贝指针和内容。这里的内容跟上文一样只针对集合对象本身,对于集合内部的元素只拷贝指针,不拷贝内容。

strong和copy

strong和copy下的字符串

先添加两个字符串ivar,分别用strong和copy修饰:

@property (nonatomic, strong) NSString *testStrongStr;
@property (nonatomic, copy) NSString *testCopyStr;

然后写个测试方法:

- (void)testCopyAndStrongForString {
    
    NSString *immutableStr = @"KFA_test4";
    self.testStrongStr = immutableStr;
    self.testCopyStr = immutableStr;
    NSLog(@"\\nimmutableStr:%@\\nself.testStrongStr:%@\\nself.testCopyStr:%@",immutableStr,self.testStrongStr,self.testCopyStr);
    immutableStr = @"KFA_test4_change";
    NSLog(@"\\nimmutableStr:%@\\nself.testStrongStr:%@\\nself.testCopyStr:%@",immutableStr,self.testStrongStr,self.testCopyStr);
    
    NSMutableString *mutableStr = [@"KFA_test5" mutableCopy];
    self.testStrongStr = mutableStr;
    self.testCopyStr = mutableStr;
    NSLog(@"\\nmutableStr:%@\\nself.testStrongStr:%@\\nself.testCopyStr:%@",mutableStr,self.testStrongStr,self.testCopyStr);
    [mutableStr appendString:@"_change"];
    NSLog(@"\\nmutableStr:%@\\nself.testStrongStr:%@\\nself.testCopyStr:%@",mutableStr,self.testStrongStr,self.testCopyStr);
}

运行结果:

immutableStr:KFA_test4
self.testStrongStr:KFA_test4
self.testCopyStr:KFA_test4 
immutableStr:KFA_test4_change
self.testStrongStr:KFA_test4
self.testCopyStr:KFA_test4 
mutableStr:KFA_test5
self.testStrongStr:KFA_test5
self.testCopyStr:KFA_test5 
mutableStr:KFA_test5_change
self.testStrongStr:KFA_test5_change
self.testCopyStr:KFA_test5

从结果可以看出,当赋值对象是不可变的字符串时,strongcopy的效果一样,因为当赋值对象为不可变的字符串时,copy修饰的ivar进行拷贝相当于上文中的对不可变对象进行copy,只拷贝指针,不考虑内容。但是当赋值对象为可变的字符串时,当赋值对象发生变化时,strong修饰的ivar会跟着变,copy修饰的ivar进行了拷贝(参考上文中对可变对象进行copy),所以不会变。这里可能有同学会有疑惑,为什么明明KFA_test4变成了KFA_test4_change,内容变了,而self.testStrongStrself.testCopyStr的内容没变,这是因为KFA_test4变成KFA_test4_change只是因为immutableStr指针进行了重指向指向了KFA_test4_change,而内容KFA_test4属于常量,存在栈区,本身并没有变。所以在修饰字符串时为了数据的安全,建议用copy,而不用strong

strong和copy下的数组

同样,先添加两个数组ivar,分别用strongcopy修饰:

@property (nonatomic, strong) NSArray *testStrongArr;
@property (nonatomic, copy) NSArray *testCopyArr;

另外再创建一个model类,有一个number属性:

@interface KFACopyModel : NSObject

@property (nonatomic, copy) NSString *number;

@end

再写两个函数(等会测试方法里会用到),用来同时打印三个数组(里面的元素):

void KFALog(NSArray *array1, NSArray *array2, NSArray *array3) {

    NSString *string1 = KFANSStringFromArray(array1);
    NSString *string2 = KFANSStringFromArray(array2);
    NSString *string3 = KFANSStringFromArray(array3);
    NSLog(@"\\n\\%@\\n%@\\n%@",string1,string2,string3);
}

NSString *KFANSStringFromArray(NSArray *array) {
    NSMutableString *string = nil;
    for (NSString *objc in array) {
        NSString *objcStr = nil;
        if ([objc isKindOfClass:[NSString class]]) {
            objcStr = (NSString *)objc;
        }else if ([objc isKindOfClass:[KFACopyModel class]]) {
            KFACopyModel *model = (KFACopyModel *)objc;
            objcStr = model.number;
        }
        if (string == nil) {
            string = [objcStr mutableCopy];
        }else {
            [string appendFormat:@"_%@",objcStr];
        }
    }
    return string;
}

然后就是测试方法了:

- (void)testCopyAndStrongForArray {
    
    NSArray *immutableArr = @[@"KFA_test6"];
    self.testStrongArr = immutableArr;
    self.testCopyArr = immutableArr;
    KFALog(immutableArr,self.testStrongArr,self.testCopyArr);
    immutableArr = @[@"KFA_test6_change"];
    KFALog(immutableArr,self.testStrongArr,self.testCopyArr);
    
    NSMutableArray *mutableArr = [@[@"KFA_test7"] mutableCopy];
    self.testStrongArr = mutableArr;
    self.testCopyArr = mutableArr;
    KFALog(mutableArr, self.testStrongArr, self.testCopyArr);
    [mutableArr replaceObjectAtIndex:0 withObject:@"KFA_test7_change"];
    KFALog(mutableArr, self.testStrongArr, self.testCopyArr);
    
    KFACopyModel *model1 = [[KFACopyModel alloc] init];
    model1.number = @"13789892310";
    NSArray *immutableArr_ = @[model1];
    self.testStrongArr = immutableArr_;
    self.testCopyArr = immutableArr_;
    KFALog(immutableArr_, self.testStrongArr, self.testCopyArr);
    model1.number = @"137****2310";
    KFALog(immutableArr_, self.testStrongArr, self.testCopyArr);
    
    KFACopyModel *model2 = [[KFACopyModel alloc] init];
    model2.number = @"13982650942";
    NSArray *mutableArr_ = @[model2];
    self.testStrongArr = mutableArr_;
    self.testCopyArr = mutableArr_;
    KFALog(mutableArr_, self.testStrongArr, self.testCopyArr);
    model2.number = @"139****0942";
    KFALog(mutableArr_, self.testStrongArr, self.testCopyArr);
}

运行结果:

KFA_test6
KFA_test6_change
KFA_test6
KFA_test6
KFA_test7
KFA_test7
KFA_test7 
KFA_test7_change
KFA_test7_change
KFA_test7 
13789892310
13789892310
13789892310 
137****2310
137****2310
137****2310 
13982650942
13982650942
13982650942 
139****0942
139****0942
139****0942

我们发现一个有意思的问题,当数组里元素为字符串时,结果跟咱们预料的一样的,但是当数组的元素为KFACopyModel对象时,结果跟咱们预料有点出入了。这里就得回到上文说的四种情况了。对集合类对象进行copymutableCopy时,即便拷贝内容,拷贝的也是集合对象本身,对于集合内的元素只拷贝了指针,不拷贝内容。所以当数组里的元素KFACopyModel对象的number属性发生变化时,拷贝的数组里元素也会跟着变化。

写在最后

通过上文我们可以总结两点:

本文用的代码全在这里,可以点击下载。欢迎拍砖和转发。

上一篇下一篇

猜你喜欢

热点阅读