夯实基础iOS 开发交流

浅谈iOS Copy与MutableCopy

2018-09-03  本文已影响208人  Mr__Peng__
500396052.jpg

写了这么久的iOS,copy与mutableCopy也用了不少,可究竟什么时候用copy,什么时候用mutablecopy,他俩区别在哪,一直一知半解,也是论坛上的某些大神说这里用copy,那就用copy,比如NSString用copy修饰符。
不谈从网上看到的深拷贝,浅拷贝概念,先从代码看起,但开始代码之前,还必须知道一个必须知道的概念:
要实现Copy 与MutableCopy必须要遵循Copy,MutableCopy协议
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>如果没有实现协议,调用copy,mutableCopy方法会Crash.

@protocol NSCopying
- (id)copyWithZone:(nullable NSZone *)zone;
@end

遵循NSCopying协议,就是实现copyWithZone方法,而关于copyWithZone
返回值id:Returns a new instance that’s a copy of the receiver.返回一个新实例
关于返回值的说明:

The returned object is implicitly retained by the sender, who is 
responsible for releasing it. The copy returned is immutable if the 
consideration “immutable vs. mutable” applies to the receiving 
object; otherwise the exact nature of the copy is determined by the 
class.

大意就是不管原始对象是不可变的或者是可变的,copy返回的都是不可变对象,copy之后的对象性质由原始对象确定。
同理,NSMutableCopying遵循协议

@protocol NSMutableCopying

- (id)mutableCopyWithZone:(nullable NSZone *)zone;

@end

返回值id:Returns a new instance that’s a mutable copy of the receiver.返回一个新实例
关于返回值说明

The returned object is implicitly retained by the sender, which is 
responsible for releasing it.
 The copy returned is mutable whether the original is mutable or not.

即不管原始对象是不是可变的,返回对象都是可变的。

NSString copy mutableCopy
    NSMutableString *string = [NSMutableString stringWithString:@"copy test"];
    NSString *origin = [NSString stringWithString:string];
    NSString *copy = [origin copy];
    NSMutableString *mut = [origin mutableCopy];
    
    NSLog(@"原始地址 %p,    %@",origin,origin);
    NSLog(@"copy地址 %p,    %@",copy,copy);
    NSLog(@"mut copy 地址 %p,    %@",mut,mut);
    [string appendString:@"aaa"];
    [mut appendString:@"ffff"];
    
    NSLog(@"原始地址 %p,    %@",origin,origin);
    NSLog(@"copy地址 %p,    %@",copy,copy);
    NSLog(@"mut copy 地址 %p,    %@",mut,mut);

输出结果

原始地址 0x883205f046fa5f3f,    copy test
copy地址 0x883205f046fa5f3f,    copy test
mut copy 地址 0x6000015efab0,    copy test
原始地址 0x883205f046fa5f3f,    copy test
copy地址 0x883205f046fa5f3f,    copy test
mut copy 地址 0x6000015efab0,    copy testffff

我们分别对俩个可以改变值进行拼接修改,[NSString stringWithString:string]方法执行的是开辟一个新的空间并拷贝string指向的值。
copy不管是原始字符串或copy后的字符串,都是不可变的,所以拷贝只需要将指针指向原始字符串的地址空间即可,即俩者是同一个玩意。
mutableCopy则需要开辟一个新的地址空间保存拷贝后的可变字符串所以地址必然变化,而由实验可知,mut改变并不会影响原来的值,得知值也是重新拷贝了一份

结论
对于不可变字符串copy之后的其实就是本身,而mutableCopy会将整个对象拷贝一份

再来看NSMutableString

NSMutableString copy mutableCopy
    NSMutableString *string = [NSMutableString stringWithString:@"copy test"];
    
    NSMutableString *originString = [[NSMutableString alloc] initWithString:string];
    NSString *copystring = [originString copy];
    NSMutableString *mutystring = [originString mutableCopy];
    
    NSLog(@"原始地址 %p,    %@",originString,originString);
    NSLog(@"copy地址 %p,    %@",copystring,copystring);
    NSLog(@"mut copy地址 %p,    %@",mutystring,mutystring);
    
     [string appendString:@"xxxxxxx"];
    [originString appendString:originString];
    [mutystring appendString:@"0000000"];
    
    NSLog(@"修改后地址 %p,    %@",originString,originString);
    NSLog(@"修改后copy地址 %p,    %@",copystring,copystring);
    NSLog(@"修改后mutcopy地址 %p,    %@",mutystring,mutystring);

输出结果

 原始地址 0x600002a55ef0,    copy test
 copy地址 0xf701af19378c9fa0,    copy test
 mut copy地址 0x600002a55bf0,    copy test
 修改后地址 0x600002a55ef0,    copy testcopy test
 修改后copy地址 0xf701af19378c9fa0,    copy test
 修改后mutcopy地址 0x600002a55bf0,    copy test0000000

由修改前输出地址可知,对于可变字符串,不管是copy,mutableCopy都会开辟新的内存空间去保存拷贝后的地址
通过修改可变字符串之后可知,俩种拷贝都会对值进行拷贝

结论
对可变字符串,copy,mutableCopy 都会将整个对象重新拷贝

NSString为什么用推荐copy比如:
一个动物类,有一个属性name,如果给name赋值的是不可变类型,皆大欢喜,copy与否都是一样的,但是当赋值的是可变类型的话

NSMutableString *originString = [[NSMutableString alloc] initWithString:@"深拷贝,浅拷贝"];
self.name = originString;
[originString appendString:@"pengshuai"];

self.name会跟着变动,而copy不会有这种顾虑,而且不管name是可变,或不可变,使用copy不会相互影响。

看来看数组拷贝:

NSArray NSMutableArray
    NSMutableArray *array = [NSMutableArray arrayWithObjects:@"yao",@"peng", nil];
    NSArray *originArr = [NSArray arrayWithObject:array];
    NSArray *copyArr = [originArr copy];
    NSMutableArray *mutableArr = [originArr mutableCopy];
    
    NSLog(@"原始数组%p     %@",originArr,originArr);
    NSLog(@"Copy数组%p     %@",copyArr,copyArr);
    NSLog(@"MutableCopy数组%p     %@",mutableArr,mutableArr);
    
    [array addObject:@"juan"];
    [mutableArr addObject:@"xx00"];
    
    NSLog(@"修改数组%p     %@",originArr,originArr);
    NSLog(@"修改Copy数组%p     %@",copyArr,copyArr);
    NSLog(@"修改MutableCopy数组%p     %@",mutableArr,mutableArr);

输出结果

原始数组0x6000001e8da0     (
    yao,
    peng
)
Copy数组0x6000001e8da0     (
    yao,
    peng
)
MutableCopy数组0x600000ff8330     (
    yao,
    peng
)
修改数组0x6000001e8da0     (
    yao,
    peng
)
修改Copy数组0x6000001e8da0     (
    yao,
    peng
)
修改MutableCopy数组0x600000ff8330     (
    yao,
    peng,
    xx00
)

得出的结果和stringcopy类型基本一样

copy 两者一样,mutableCopy 对象拷贝一份
再看NSMutableArray

    NSMutableArray *array = [NSMutableArray arrayWithObjects:@"yao",@"peng", nil];
    NSMutableArray *originArray = [NSMutableArray arrayWithArray:array];
    NSArray *copyArray = [originArray copy];
    NSMutableArray *mutableArray = [originArray mutableCopy];
    
    NSLog(@"原始数组%p     %@",originArray,originArray);
    NSLog(@"Copy数组%p     %@",copyArray,copyArray);
    NSLog(@"MutableCopy数组%p     %@",mutableArray,mutableArray);
    
    [array addObject:@"juan"];
    [originArray addObject:@"xxx"];
    [mutableArray addObject:@"000"];
    
    NSLog(@"修改数组%p     %@",originArray,originArray);
    NSLog(@"修改Copy数组%p     %@",copyArray,copyArray);
    NSLog(@"修改MutableCopy数组%p     %@",mutableArray,mutableArray);

输出结果

原始数组0x600001ec8810     (
    yao,
    peng
)
Copy数组0x6000010daf40     (
    yao,
    peng
)
MutableCopy数组0x600001ec86f0     (
    yao,
    peng
)
修改数组0x600001ec8810     (
    yao,
    peng,
    xxx
)
修改Copy数组0x6000010daf40     (
    yao,
    peng
)
修改MutableCopy数组0x600001ec86f0     (
    yao,
    peng,
    000
)

对于可变数组,可以看到不管哪种copy,都会对对象重新拷贝,和可变字符串一样啊,但是,数组容器有一个变数,如下:

  NSMutableArray *array = [NSMutableArray arrayWithObjects:@"yao",@"peng", nil];
    NSMutableArray *originArray = [NSMutableArray arrayWithObject:array];
    NSArray *copyArray = [originArray copy];
    NSMutableArray *mutableArray = [originArray mutableCopy];
    
    NSLog(@"原始数组%p     %@",originArray,originArray);
    NSLog(@"Copy数组%p     %@",copyArray,copyArray);
    NSLog(@"MutableCopy数组%p     %@",mutableArray,mutableArray);
    
    [array addObject:@"juan"];
    [originArray addObject:@"xxx"];
    [mutableArray addObject:@"000"];
    
    NSLog(@"修改数组%p     %@",originArray,originArray);
    NSLog(@"修改Copy数组%p     %@",copyArray,copyArray);
    NSLog(@"修改MutableCopy数组%p     %@",mutableArray,mutableArray);

在看结果

原始数组0x60000177d530     (
        (
        yao,
        peng
    )
)
2019-01-03 16:15:08.055989+0800 ddd[16899:1986944] Copy数组0x600001b3c950     (
        (
        yao,
        peng
    )
)
2019-01-03 16:15:08.056074+0800 ddd[16899:1986944] MutableCopy数组0x60000177d1d0     (
        (
        yao,
        peng
    )
)
修改数组0x60000177d530     (
        (
        yao,
        peng,
        juan
    ),
    xxx
)
修改Copy数组0x600001b3c950     (
        (
        yao,
        peng,
        juan
    )
)
修改MutableCopy数组0x60000177d1d0     (
        (
        yao,
        peng,
        juan
    ),
    000
)

其他都类似,但是改变array的时候,不管哪种拷贝,数组也会跟着改变,是因为数组复制,其元素对象始终是指针复制,元素指向的值改变,数组自然都会改变。

由以上实验大概得出以下结论

非集合对象 copy mutable copy
不可变对象 指针拷贝 指针拷贝,值拷贝
可变对象 指针拷贝,值拷贝 指针拷贝,值拷贝
集合对象 copy mutable copy
不可变对象 指针拷贝 指针拷贝,元素对象指针拷贝
可变对象 指针拷贝,元素对象指针拷贝 指针拷贝,元素对象指针拷贝
Block 与 Copy

使用block属性的时候一般也使用copy,一般情况下,block默认保存在栈区,在对外部对象进行操作时,不会对对象进行retain,而当block保存在堆区时,在外部对象进行操作时,会对对象进行retain。而我们本是是不知道什么时候什么时候调用block的,当block中的对象提前释放,会造成Crash,使用copy关键字能保住block,防止调用的时候Crash.

上一篇下一篇

猜你喜欢

热点阅读