ios开发基础学习笔记(十)--copy详解

前言

大家好,我是milo。本篇文章主要围绕着copy这一关键字讲解,包括copy和mutableCopy的区别、浅拷贝和深拷贝的区别、@property的copy、自定义类实现copy、继承父类实现copy的讲解。我将通过先给出结论再进行验证的方式说明,避免大家凌乱混淆。

copy和mutableCopy的区别

如何使用copy功能

一个对象可以调用copy或mutableCopy方法来创建一个副本对象。如下面一个简单的示范:

    NSString *str1 = @"hello";
    NSString *str2 = [str1 copy];
    NSLog(@"%@ , %@",str1,str2);//    打印 “hello , hello”

非常简单,像对象方法一样调用就可以使用copy功能。

但是我们需要注意的是:
copy : 创建的是不可变副本(如NSString、NSArray、NSDictionary)
mutableCopy :创建的是可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary)

一句话作为结论:copy生成的是不可变对象,mutableCopy生成的是可变对象

下面这张图是不同源对象调用copy或mutableCopy时产生的副本,也对应了上面这句结论。


copy和mutableCopy生成的副本图.png

下面我们将对上图进行验证,如果结果如上图所示那样,那么结论便是正确的,即:copy生成的是不可变对象,mutableCopy生成的是可变对象

虽说结论是最重要的,但是我也推荐大家花个几分钟看完验证过程,才会真正理解结论。由于NSString、NSArray、NSDictionary得出的结论相同,我就只对NSString进行验证,以下为验证过程:

NSString调用Copy:

    NSString *str1 = @"hello";
    NSMutableString *str2 = [str1 copy];
//将copy生成的对象赋值给NSMutableString,再对其进行修改,如果报错,说明生成的是不可变对象
    [str2 appendString:@"world"];//这里报错了,说明生成的是不可变对象
    NSLog(@"%@ , %@",str1,str2);

同样地,将上段代码的copy改为mutableCopy进行验证,如下:

NSString调用mutableCopy:

    NSString *str1 = @"hello";
    NSMutableString *str2 = [str1 mutableCopy];
//将copy生成的对象赋值给NSMutableString,再对其进行修改,如果报错,说明生成的是不可变对象
    [str2 appendString:@"world"];//这里没有报错,说明生成的是可变对象
    NSLog(@"%@ , %@",str1,str2);// 打印 “hello , helloworld”

综合NSString调用拷贝方法情况来看,NSString通过copy产生的实际是NSString,如果把NSString赋值给NSMutableString指针并且修改它,就会报错;而通过mutableCopy产生的实际是NSMutableString,就可以进行修改,虽然赋值给NSString指针不会错,但是就变成了不可变的了,那使用mutableCopy就没意义了,所以要避免。

同样的方法,再对NSMutableString进行copy和mutableCopy调用。
NSMutableString调用copy

    NSMutableString *str1 = [NSMutableString stringWithString:@"hello"];
    NSMutableString *str2 = [str1 copy];
//将copy生成的对象赋值给NSMutableString,再对其进行修改,如果报错,说明生成的是不可变对象
    [str2 appendString:@"world"];//这里报错,说明生成的是不可变对象
    NSLog(@"%@ , %@",str1,str2);

NSMutableString调用mutableCopy

    NSMutableString *str1 = [NSMutableString stringWithString:@"hello"];
    NSMutableString *str2 = [str1 mutableCopy];
//将copy生成的对象赋值给NSMutableString,再对其进行修改,如果报错,说明生成的是不可变对象
    [str2 appendString:@"world"];//这里没有报错,说明生成的是可变对象
    NSLog(@"%@ , %@",str1,str2);// 打印 “hello , helloworld”

综合NSMutableString调用拷贝方法情况来看,和前面的结论也一致,就是无论是谁调用copy生成的都是生成不可变对象,无论谁调用mutableCopy生成的都是可变对象。

小结:总之,一句话,copy生成的是不可变对象,mutableCopy生成的是可变对象!!

浅拷贝和深拷贝的区别

网上很多文章会把copy当作浅拷贝,mutableCopy当作深拷贝,在这里我提醒大家,不是这么回事的!

浅拷贝和深拷贝的区别.png

浅拷贝和深拷贝是从内存的角度来进行区分的,如果仅仅以copy和mutableCopy区分,那是不对的,正确区分浅拷贝和深拷贝的结论应该是如下:

结论:只有源对象和副本对象都不可变时,才是浅拷贝,其它都是深拷贝。

如NSString通过copy赋值给NString,是浅拷贝,因为都不可变,所以只是指针拷贝(编译器优化)就可以不影响数值了,因为NSString原则上说不可变;而NSString通过mutableCopy赋值给NSMutableString就是深拷贝,需要生成一个完整的副本。

//    NSString部分
    NSString *str1 = @"hello";
    
    NSString *str2 = [str1 copy];
    NSLog(@"%p,%p",str1,str2);// 地址相同,浅拷贝
    
    NSString *str3 = [str1 mutableCopy];
    NSLog(@"%p,%p",str1,str3);// 地址不相同,深拷贝
    
//    NSMutableString部分
    NSMutableString *str4 = [NSMutableString stringWithFormat:@"hello"];
    
    NSString *str5 = [str4 copy];
    NSLog(@"%p,%p",str4,str5);// 地址不相同,深拷贝

    NSMutableString *str6 = [str4 copy];
    NSLog(@"%p,%p",str4,str6);// 地址不相同,深拷贝

通过上面一段代码,我们就可以得到不同的情况。但都只有一种情况是浅拷贝,即NSString对象拷贝产生NSString对象的时候,此时是指针拷贝。由于编译器会对浅拷贝做优化,所以后面即使有修改,也不会影响彼此,因为编译器让其变成了深拷贝。

小结即结论:只有源对象和副本对象都不可变时,才是浅拷贝,其它都是深拷贝。

@property的copy

当我们在使用@property声明字符串的时候,都是像下面这样声明:

@property (nonatomic, copy) NSString *name;

其中,为什么要用copy,而不是用retain呢?
答:因为我们声明的是不可变类型NSString,不希望在赋值以后做其他更改,所以用copy,无论是浅拷贝和深拷贝,都不会受到别人(常指NSMutableString)的影响。而如果用retain,就会在赋值好以后,可能被NSMutableString改掉,因为retain是索引计数+1的声明方式,属于新增一个指针对象指向内存。

所以结论就是:
使用copy修饰不可变对象,就可以防治被无意中修改。

我们可以将copy修改为retain验证下:

    JJPerson *person = [[JJPerson alloc] init];
    NSMutableString *name = [NSMutableString stringWithString:@"milo"];
    [person setName:name];
    NSLog(@"%@",[person name]);//    打印 “milo”
    
    [name appendString:@"&vicky"];
    NSLog(@"%@",[person name]);//    打印 “milo&vikcy”

我们在赋值以后,再对外部的name修改时,[person name] 啥事也没干就被改了(想想你的名字取好了,又被别人无意改了一下,气不气)

所以我们在声明NSString、NSArray、NSDictionary这些不可变对象的时候,一定要用copy,防止被更改。但当我们想使用NSMutableString、NSMutableArray、NSMutableDictionary这些可变对象的时候,就要对应使用retain了,因为它们就是想要被改!

小结:
1、copy修饰不可变对象,就可以防治被无意中修改
2、retain修饰可变对象,因为它们就是想被改

自定义类实现copy

前面都是在以NSString为例举各种关于copy的例子,我们如果想让自定义的类也实现拷贝,就需要:
1、有一个类
2、实现相应协议,想用copy,则遵守NSCopying协议,想用mutableCopy,则遵守NSMutableCopying协议
3、实现协议中的copyWithZone方法或mutableCopyWithZone方法
4、调用

除了3需要上段代码看看如何实现以外,其他都不用说了:

-(id)copyWithZone:(NSZone *)zone {
    JJPerson *person = [[JJPerson alloc] init];
    [person setAge:self.age];
    [person setName:self.name];
    return person;
}

非常简单对吧!就是把当前属性的值,重新赋值给新对象。而zone参数不用去理会,它是个古老的技术。

继承父类实现copy

这一部分跟前面的“自定义类实现copy”部分相关联,就是当子类继承父类时的拷贝实现。

只需要记住一个结论:
子类继承父类时,如果有新增属性,那么copy方法就要重写,否则新增属性值就是空的。

子类实现部分:

-(id)copyWithZone:(NSZone *)zone {    
    JJStudent *student = [super copyWithZone:zone];
    [student setSId:self.sId];
    return student;
}

父类也需要做如下改动:

-(id)copyWithZone:(NSZone *)zone {

    // 只能copy出Person对象
    // Person *p = [[Person alloc] init];

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

推荐阅读更多精彩内容