天天看点

OC语法——Object-C retain、copy、mutableCopy的详细分析

OC语法中的retain、copy、mutableCopy 大家都基本知道它的基本意思,但是对于mutable类型和immutable类型的处理上有很多童鞋并没有真正测试过,今天就和大家分享下:

1.先来看下immutable类型的非容器类: NSString的retain、copy和mutableCopy的测试

NSString *string = @"abc";
    
    NSString *retainString = [string retain];
    
    NSString *copyString = [string copy];

    NSString *mCopyString = [string mutableCopy];
           

我们打印下每个变量的值 + 值的地址 + 指向该值的指针地址:

NSLog(@"string:%@ content:%p p:%p", string, string, &string);
    NSLog(@"retainString:%@ content:%p p:%p", retainString, retainString, &retainString);
    NSLog(@"copyString:%@ content:%p p:%p", copyString, copyString, &copyString);
    NSLog(@"mCopyString:%@ content:%p p:%p", mCopyString, mCopyString, &mCopyString);
           

这个很简单,结果如下:

2015-08-07 18:10:44.270 MyTest[834:40452] string:abc content:0x10008c098 p:0x16fd7dcf8
2015-08-07 18:10:44.271 MyTest[834:40452] retainString:abc content:0x10008c098 p:0x16fd7dcf0
2015-08-07 18:10:44.271 MyTest[834:40452] copyString:abc content:0x10008c098 p:0x16fd7dce8
2015-08-07 18:10:44.271 MyTest[834:40452] mCopyString:abc content:0x17807ce00 p:0x16fd7dce0
           

从日志可以看出:retain、copy、mutableCopy都复制了指针;不同的是retain、copy指针还是指向原值地址,而mutableCopy则是复制了对象。也就是说:retain、copy对immutable 非容器类对象进行了“浅拷贝”,而mutableCopy则是“深拷贝”。

还有一个点需要注意下:对NSString进行了mutableCopy后得到的mCopyString是mutable还是immutable类型的呢?试一下:

[(NSMutableString *)mCopyString appendFormat:@"def"];
           

得到的结果是:

2015-08-08 19:05:15.873 MyTest[865:27257] mCopyString:abcdef content:0x7ffab349c460 p:0x7fff5b4e76c0
           

也就是说:对immutable的NSString进行了mutableCopy后,变成了mutable类型的。

2。再按照相同的方式来查看下mutable类型的非容器类:NSMutableString

NSMutableString *string = [NSMutableString stringWithFormat:@"abc"];
    
    NSMutableString *retainString = [string retain];
    
    NSMutableString *copyString = [string copy];
    
    NSMutableString *mCopyString = [string mutableCopy];
           

打印下每个变量的指针地址和值的地址:

NSLog(@"string:%@ content:%p p:%p", string, string, &string);
    NSLog(@"retainString:%@ content:%p p:%p", retainString, retainString, &retainString);
    NSLog(@"copyString:%@ content:%p p:%p", copyString, copyString, &copyString);
    NSLog(@"mCopyString:%@ content:%p p:%p", mCopyString, mCopyString, &mCopyString);
           

结果:

2015-08-08 19:16:47.630 MyTest[954:32260] string:abc content:0x7ff1c97a07a0 p:0x7fff534486a8
2015-08-08 19:16:47.630 MyTest[954:32260] retainString:abc content:0x7ff1c97a07a0 p:0x7fff534486a0
2015-08-08 19:16:47.631 MyTest[954:32260] copyString:abc content:0x7ff1c97a07e0 p:0x7fff53448698
2015-08-08 19:16:47.631 MyTest[954:32260] mCopyString:abc content:0x7ff1c97a0820 p:0x7fff53448690
           

从日志能看出:对NSMutableString进行的操作,copy和mutableCopy进行了“深拷贝”,而retain是“浅拷贝”。

我们改变下string来测试下:

[string appendFormat:@"de"];
    NSLog(@"string:%@ content:%p p:%p", string, string, &string);
    NSLog(@"retainString:%@ content:%p p:%p", retainString, retainString, &retainString);
    NSLog(@"copyString:%@ content:%p p:%p", copyString, copyString, &copyString);
    NSLog(@"mCopyString:%@ content:%p p:%p", mCopyString, mCopyString, &mCopyString);
           

结果:

2015-08-08 19:23:14.497 MyTest[1020:35070] string:abcde content:0x7fbdf9e936b0 p:0x7fff5c1656a8
2015-08-08 19:23:14.497 MyTest[1020:35070] retainString:abcde content:0x7fbdf9e936b0 p:0x7fff5c1656a0
2015-08-08 19:23:14.497 MyTest[1020:35070] copyString:abc content:0x7fbdf9e928b0 p:0x7fff5c165698
2015-08-08 19:23:14.497 MyTest[1020:35070] mCopyString:abc content:0x7fbdf9e913b0 p:0x7fff5c165690
           

证明:copy和mutableCopy确实是“深拷贝”,所以他们的值还是“abc”。

那retain、copy、和mutableCopy后是mutable类型还是immutable类型的呢?让我们测试下:

[retainString appendFormat:@"de"]; //正确,因为retain后值的地址没有改变
    [copyString appendFormat:@"de"]; //错误导致crash,因为copy是深拷贝,新值是immutable的
    [mCopyString appendFormat:@"de"]; //正确,因为mutableCopy是深拷贝,新值是mutable的
           

第二个如果运行会导致崩溃,也就是说:NSMutableString进行copy后是immutable类型的,进行mutableCopy后是mutable类型的。

所以如下的申明方式是错误的,mutableString是immutable类型的。

@property(nonatomic, copy) NSMutableString *mutableString;
           

3。再来看下immutable类型的容器类:NSArray

NSArray *array = @[@"a", @"b", @"c"];
    NSArray *retainArray = [array retain];
    NSArray *copyArray = [array copy];
    NSArray *mCopyArray = [array mutableCopy];
           
NSLog(@"array:%@ content:%p p:%p", array, array, &array);
    NSLog(@"retainArray:%@ content:%p p:%p", retainArray, retainArray, &retainArray);
    NSLog(@"copyArray:%@ content:%p p:%p", copyArray, copyArray, &copyArray);
    NSLog(@"mCopyArray:%@ content:%p p:%p", mCopyArray, mCopyArray, &mCopyArray);
           

结果:

2015-08-08 19:39:16.840 MyTest[1199:42450] array:(
    a,
    b,
    c
) content:0x7f92e0e4e870 p:0x7fff4fc06660
2015-08-08 19:39:16.841 MyTest[1199:42450] retainArray:(
    a,
    b,
    c
) content:0x7f92e0e4e870 p:0x7fff4fc06658
2015-08-08 19:39:16.842 MyTest[1199:42450] copyArray:(
    a,
    b,
    c
) content:0x7f92e0e4e870 p:0x7fff4fc06650
2015-08-08 19:39:16.842 MyTest[1199:42450] mCopyArray:(
    a,
    b,
    c
) content:0x7f92e0c89f30 p:0x7fff4fc06648
           

从结果来看可以证明:对于immutable类型的容器类NSArray,retain、copy是“浅拷贝”,mutableCopy是“深拷贝”。同样的:mCopyArray是一个mutable类型的

[(NSMutableArray *)mCopyArray addObject:@"d"]; //正确
           

4。最后是mutable 容器类NSMutableArray的测试:

NSMutableString *mString = [NSMutableString stringWithFormat:@"a"];
    
    NSMutableArray *array = [NSMutableArray arrayWithObjects:mString, @"b", @"c", nil];
    NSMutableArray *retainArray= [array retain];
    NSMutableArray *copyArray = [array copy];
    NSMutableArray *mCopyArray = [array mutableCopy];
           
NSLog(@"array:%@ content:%p p:%p", array, array, &array);
    NSLog(@"retainArray:%@ content:%p p:%p", retainArray, retainArray, &retainArray);
    NSLog(@"copyArray:%@ content:%p p:%p", copyArray, copyArray, &copyArray);
    NSLog(@"mCopyArray:%@ content:%p p:%p", mCopyArray, mCopyArray, &mCopyArray);
           

打印结果:

2015-08-08 19:53:30.541 MyTest[1319:49288] array:(
    a,
    b,
    c
) content:0x7f8f71f45190 p:0x7fff5467f6a0
2015-08-08 19:53:30.541 MyTest[1319:49288] retainArray:(
    a,
    b,
    c
) content:0x7f8f71f45190 p:0x7fff5467f698
2015-08-08 19:53:30.542 MyTest[1319:49288] copyArray:(
    a,
    b,
    c
) content:0x7f8f71f45210 p:0x7fff5467f690
2015-08-08 19:53:30.542 MyTest[1319:49288] mCopyArray:(
    a,
    b,
    c
) content:0x7f8f71f45240 p:0x7fff5467f688
           

可以看出:同NSMutableString相同,retain是“浅拷贝”,copy、mutableCopy是“深拷贝”。

同样的:

[retainArray addObject:@"d"];//正确
    [copyArray addObject:@"d"]; //错误导致crash
    [mCopyArray addObject:@"d"];//正确
           

还有个很重要的问题:容器类可以包含别的对象,那进行“深拷贝”时它包含的对象是“浅拷贝”还是“深拷贝“呢,让我们来看下:

NSMutableString *mString = [NSMutableString stringWithFormat:@"a"];
    
    NSMutableArray *array = [NSMutableArray arrayWithObjects:mString, @"b", @"c", nil];
    NSMutableArray *copyArray = [array copy];
    NSMutableArray *mCopyArray = [array mutableCopy];
           

mString是array的第一个对象,然后对array进行了”深拷贝“,打印下:

NSLog(@"array:%@ content:%p p:%p", array, array, &array);
    NSLog(@"copyArray:%@ content:%p p:%p", copyArray, copyArray, &copyArray);
    NSLog(@"mCopyArray:%@ content:%p p:%p", mCopyArray, mCopyArray, &mCopyArray);
           
2015-08-08 20:01:23.624 MyTest[1413:53021] array:(
    a,
    b,
    c
) content:0x7f8150473240 p:0x7fff523436a0
2015-08-08 20:01:23.625 MyTest[1413:53021] copyArray:(
    a,
    b,
    c
) content:0x7f8150473270 p:0x7fff52343698
2015-08-08 20:01:23.625 MyTest[1413:53021] mCopyArray:(
    a,
    b,
    c
) content:0x7f81504732a0 p:0x7fff52343690
           

然后我们把mString的值改变一下:

[mString appendFormat:@"aaa"];
           

再打印下:

NSLog(@"array:%@ content:%p p:%p", array, array, &array);
    NSLog(@"copyArray:%@ content:%p p:%p", copyArray, copyArray, &copyArray);
    NSLog(@"mCopyArray:%@ content:%p p:%p", mCopyArray, mCopyArray, &mCopyArray);
           

让我们看下结果:

2015-08-08 20:01:23.626 MyTest[1413:53021] array:(
    aaaa,
    b,
    c
) content:0x7f8150473240 p:0x7fff523436a0
2015-08-08 20:01:23.626 MyTest[1413:53021] copyArray:(
    aaaa,
    b,
    c
) content:0x7f8150473270 p:0x7fff52343698
2015-08-08 20:01:23.626 MyTest[1413:53021] mCopyArray:(
    aaaa,
    b,
    c
) content:0x7f81504732a0 p:0x7fff52343690
           

结果很显然:“深拷贝”后的数组里面的对象还是原来的对象,所以“深拷贝”后的第一个对象的值都变成了aaaa。

那怎么让“深拷贝”的数组里面的元素也是“深拷贝呢”?也是有方法的:

- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag;
           

测试下:

NSMutableString *mString = [NSMutableString stringWithFormat:@"a"];
    
    NSMutableArray *array = [NSMutableArray arrayWithObjects:mString, @"b", @"c", nil];
    NSMutableArray *mDeepCopyarray = [[NSMutableArray alloc] initWithArray:array copyItems:YES];

    NSLog(@"mDeepCopyarray:%@ content:%p p:%p", mDeepCopyarray, mDeepCopyarray, &mDeepCopyarray);

    [mString appendFormat:@"aaa"];
    
    NSLog(@"array:%@ content:%p p:%p", array, array, &array);
    NSLog(@"mDeepCopyarray:%@ content:%p p:%p", mDeepCopyarray, mDeepCopyarray, &mDeepCopyarray);
           

结果:

2015-08-08 20:09:52.781 MyTest[1492:57289] mDeepCopyarray:(
    a,
    b,
    c
) content:0x7f8988deec60 p:0x7fff5025c698
2015-08-08 20:09:52.782 MyTest[1492:57289] array:(
    aaaa,
    b,
    c
) content:0x7f8988deec30 p:0x7fff5025c6a0
2015-08-08 20:09:52.782 MyTest[1492:57289] mDeepCopyarray:(
    a,
    b,
    c
) content:0x7f8988deec60 p:0x7fff5025c698
           

成功的进行了数组元素的“深拷贝”。