copy和mutableCopy

捡起N久之前的知识整理一下

1.拷贝语法的目的

改变副本的时候,不会影响到源对象
深拷贝:内容拷贝,会产生新的对象。新对象计数器置为1,源对象计数器不变。
浅拷贝:指针拷贝,不会产生新的对象。源对象计数器+1。

2.深拷贝or浅拷贝

拷贝一个object也就是创建一个新的实例,并且初始化为拷贝源的值。对于像boolean,integer这类值,拷贝就是直接赋值。对于指针形的object就分为浅拷贝和深拷贝。浅拷贝是只创建一个新的指针,并指向同一块数据。深拷贝就是数据和指针都创建。

3.copy和retain的区别

retain不创建指针,不创建对象,只是引用计数+1。
copy创建指针,指针指向旧对象的区域。新对象引用计数为1,一定程度上减少了对旧对象的依赖。

非容器类对象的拷贝
NSString *str = @"helloWorld";
NSString *strCopy = [str copy];
NSLog(@"%p  %p",str,strCopy);
//打印结果:0x100517068  0x100517068
NSMutableString *str = [[NSMutableString alloc]initWithString:@"helloWorld"];
NSString *strCopy = [str copy];
NSLog(@"%p  %p",str,strCopy);
//打印结果:0x6080000786c0  0x6080000399e0

copy不可变对象:浅拷贝
copy可变对象:深拷贝,but!返回的是不可变对象

NSString *str = @"helloWorld";
NSMutableString *strMutCopy = [str mutableCopy];
[strMutCopy appendFormat:@"!!!"];
NSLog(@"%p  %p  %@",str,strMutCopy,strMutCopy);
//打印结果:0x100c25068  0x60000007a5c0  helloWorld!!!
NSMutableString *str = [[NSMutableString alloc]initWithString:@"helloWorld"];
NSMutableString *strMutCopy = [str mutableCopy];
[strMutCopy appendFormat:@"!!!"];
NSLog(@"%p  %p  %@",str,strMutCopy,strMutCopy);
//打印结果:0x600000264140  0x600000264180  helloWorld!!!

mutableCopy不可变对象or可变对象:都是深拷贝,返回的都是可变对象

对于非容器类对象:copy可变对象返回可变对象,copy不可变对象返回不可变对象,mutableCopy可变不可变都可以返回可变对象。

容器类对象的拷贝
NSArray *array = @[@"1",@"2",@"3"];
NSArray *arrayCopy = [array copy];
NSLog(@"%p  %p",array,arrayCopy);
//打印结果:0x60000004f5a0  0x60000004f5a0
NSArray *array = @[@"1",@"2",@"3"];
NSMutableArray *arrayMutableCopy = [array mutableCopy];
[arrayMutableCopy addObject:@"4"];
[arrayMutableCopy replaceObjectAtIndex:0 withObject:@"first"];
NSLog(@"%p  %p  %@",array,arrayMutableCopy,arrayMutableCopy);
/**打印结果:0x600000054250  0x600000053e90  (
    first,
    2,
    3,
    4
)
*/

从以上结果来看,貌似只要对数组进行了mutableCopy,貌似数组和数组元素都深拷贝了......
然鹅......并不是酱紫,是因为我们对数组元素进行了replaceObjectAtIndex操作导致的地址重新创建了。其实,单纯的对数组进行mutableCopy,数组元素是浅拷贝的。于是,用以下代码进行了验证

NSArray *array = @[@"1",@"2",@"3"];
NSArray *arrayCopy = [array copy];
NSMutableArray *arrayMutableCopy = [array mutableCopy];
NSLog(@"%p  %p  %p",array[0],arrayCopy[0],arrayMutableCopy[0]);
//打印结果:0x10f2d5070  0x10f2d5070  0x10f2d5070

所以,如果对容器类对象进行深拷贝连同元素都是创建一个新的地址的话,就要进行如下操作了

NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"1"],@"2",@"3", nil];
NSArray *deepCopyArray = [[NSArray alloc]initWithArray:array copyItems:YES];
NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:array]];
NSLog(@"%p  %p  %p",array[0],array[1],array[2]);
NSLog(@"%p  %p  %p",deepCopyArray[0],deepCopyArray[1],deepCopyArray[2]);
NSLog(@"%p  %p  %p",trueDeepCopyArray[0],trueDeepCopyArray[1],trueDeepCopyArray[2]);
/**打印结果:
0x608000073140  0x10991b080  0x10991b0a0
0xa000000000000311  0x10991b080  0x10991b0a0
0x608000073440  0xa000000000000321  0xa000000000000331
*/

对于容器类对象:
1.可以用“先归档再解归档”的方法实现容器类及其元素的深拷贝。是真正意义上的深拷贝。
2.initWithArray: copyItems:方法会对可变元素进行深拷贝,不可变元素进行浅拷贝。

自定义对象的拷贝

自定义对象实现拷贝,需要遵循<NSCopying><NSMutableCopying>协议,实现两个方法
- (id)copyWithZone:(NSZone *)zone //可变拷贝
- (id)mutableCopyWithZone:(NSZone *)zone //不可变拷贝

对象内部方法实现:
.h
@property(nonatomic,copy)NSString *name;
@property(nonatomic,strong)NSMutableString *variety;
@property(nonatomic,assign)NSInteger age;
.m
//重写init方法
-(instancetype)init
{
    if (self = [super init]) {
        self.name = @"doggie";
        self.variety = [[NSMutableString alloc]initWithString:@"金毛"];
        self.age = 3;
    }
    return self;
}
//遵循NSCopying协议才能实现此方法
-(id)copyWithZone:(NSZone *)zone
{
    Dog *dog = [[self class]allocWithZone:zone];
    dog.name = [_name copy];
    dog.variety = [_variety copy];
    //基本数据类型直接赋值
    dog.age = _age;
    return dog;
}
//遵循NSMutableCopying协议才能实现此方法
-(id)mutableCopyWithZone:(NSZone *)zone
{
    Dog *dog = [[self class]allocWithZone:zone];
    dog.name = [_name mutableCopy];
    dog.variety = [_variety mutableCopy];
    dog.age = _age;
    return dog;
}
测试代码:
    Dog *dog1 = [[Dog alloc]init];
    Dog *dog2 = [dog1 copy];
    Dog *dog3 = [dog1 mutableCopy];
    NSLog(@"%p %p",dog1.variety,dog1.name);
    NSLog(@"%p %p",dog2.variety,dog2.name);
    NSLog(@"%p %p",dog3.variety,dog3.name);
/**输出结果:
 0x7fdbba4a2910 0x107221098
 0x7fdbba4a2910 0x107221098
 0x7fdbba4a2ad0 0xa00656967676f646
*/
图片发自简书App
图片发自简书App
图片发自简书App

总结一下
1.copy返回的都是不可变对象
2.可变对象都是深拷贝
3.不可变对象copy是浅拷贝,mutableCopy是深拷贝
4.容器对象不用特殊的方法都是单层拷贝

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容