iOS进阶之深拷贝和浅拷贝

前言

在开发的时候,使用copy的频率还是挺高的,我们只要copy定义的属性的设置方法并不保留新值,只是其拷贝一份值,为什么NSString、NSArray、NSDictionary属性的定义说那个copy,如果使用strong关键字有什么问题?所以这节就讲一下以及什么使用深拷贝和浅拷贝的问题。

概念

  • 浅拷贝:浅拷贝就是指针拷贝,就是拷贝一份指向该对象的指针,就是复制的对象和原对象都指向同一个地址
  • 深拷贝:深拷贝是内容拷贝,真正的复制一份,复制对象的内容。复制的对象指向新的地址。如下图:
深拷贝和浅拷贝的示意图.png

copy和mutablecopy概念

  • copy:copy拷贝出来的对象类型总是不可变类型(例如, NSString, NSDictionary, NSArray等等)

  • mutableCopy拷贝出来的对象类型总是可变类型(例如, NSMutableString, NSMutableDictionary, NSMutableArray等等)

  • 对象要想具有copy和mutablecopy功能要是NSCopying和NSMutableCopy协议,实现copywithzone和mutablecopywithzone

非集合类对象的copy与mutableCopy

系统非集合类对象指的是 NSString, NSNumber ... 之类的对象。下面先看个非集合类immutable对象拷贝的例子

NSString *string = @"origin";
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];

通过查看内存,可以看到 stringCopy 和 string 的地址是一样,进行了指针拷贝;而 stringMCopy 的地址和 string 不一样,进行了内容拷贝;

再看mutable对象拷贝例子:

NSMutableString *string = [NSMutableString stringWithString: @"origin"];
//copy
NSString *stringCopy = [string copy];
NSMutableString *mStringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
//change value
[mStringCopy appendString:@"mm"]; //crash
[string appendString:@" origion!"];
[stringMCopy appendString:@"!!"];

运行以上代码,会在第7行crash,原因就是 copy 返回的对象是 immutable 对象。注释第7行后再运行,查看内存,发现 string、stringCopy、mStringCopy、stringMCopy 四个对象的内存地址都不一样,说明此时都是做内容拷贝.

在非集合类对象中得出结论:

  • 对immutable对象进行copy操作,是指针复制mutableCopy操作时内容复制
  • 对mutable对象进行copy和mutableCopy都是内容复制
  • 可变对象通过copy之后就变成不可变了对象。

集合类对象的copy与mutableCopy

  • 对不可变集合类:

    • 不可变集合类对象是指NSArray、NSDictionary、NSSet ... 之类的对象。下面先看集合类immutable对象使用copy和mutableCopy的一个例子:
    NSArray *array = @[@[@"a", @"b"],   @[@"c", @"d"];
    NSArray *copyArray = [array copy];
    NSMutableArray *mCopyArray = [array     mutableCopy];
    [mCopyArray addobject: @"e"];
    
    

    查看内容,可以看到copyArray和array的地址是一样的,而mCopyArray和array的地址是不同的。说明copy操作进行了指针拷贝,mutableCopy进行了内容拷贝。mCopyArray可以添加对象,是可变对象

    • 强调:此处的内容拷贝,仅仅是拷贝array这个对象,array集合内部的元素仍然是指针拷贝。
  • 对可变集合类:

    • 可变集合类对象是指NSMutableArray、NSMutableDictionary、NSMutableSet ... 之类的对象。下面先看集合类immutable对象使用copy和mutableCopy的一个例子
    NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
    NSArray *copyArray = [array copy];
    NSMutableArray *m1CopyArray = [array copy];
    NSMutableArray *mCopyArray = [array mutableCopy];
    [mCopyArray addObject:@"d"];
    [m1CopyArray addObject:@"d"]; //crash
    
    

    查看内存,

    
    (lldb) po array
    

<__NSArrayM 0x600001963540>(
a,
b,
c
)

(lldb) po copyArray

<__NSArrayI 0x6000019635d0>(
a,
b,
c
)

(lldb) po mCopyArray

<__NSArrayM 0x600001963390>(
a,
b,
c,
d
)

(lldb) po m1CopyArray

<__NSArrayI 0x600001963570>(
a,
b,
c
)

```
打印的地址可以得出结论:

* 在集合类对象中,对immutable对象进行copy,是指针复制,mutableCopy是内容复制。
*  在集合类对象中,对mutable对象进行copy和mutableCopy都是内容复制。
* 在集合类对象中,对对象进行copy的对象就是不可变的,进行mutable就是可变的

为啥对NSString、NSArray、NSDictionary进行copy,如果进行strong处理有什么后果

copy 此特质所表达的所属关系与 strong 类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为 NSString 时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个 NSMutableString 类的实例。这个类是 NSString 的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。例如:

//定义一个以 strong 修饰的 array:
@property (nonatomic ,readwrite, strong) NSArray *array;

进行如下操作

   NSArray *array = @[ @1, @2, @3, @4 ];
   NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array];
   
   self.array = mutableArray;
   [mutableArray removeAllObjects];;
   NSLog(@"self.array:%@",self.array);
   
   [mutableArray addObjectsFromArray:array];
   self.array = [mutableArray copy];
   [mutableArray removeAllObjects];;
   NSLog(@"self.array:%@",self.array);
   

打印结果如下所示:

self.array:(
),
self.array:(
   1,
   2,
   3,
   4
)

结果说明如果使用strong来定义不可变对象,它的子类可变对象,有可能该对象的指针指向他的子类,他的子类改变了,该对象也就改变了。这样就不经意篡改了该对象的值,不安全了。

总结

通过本章了解,可以大概总结一下几点:

  • 对于不可变对象(不可变对象和不可变集合对象),进行copy都是指针拷贝,进行mutablecopy都是内容拷贝。

  • 对于可变对象(可变对象和可变集合对象),进行copy和mutablecopy都是内容copy。

  • 不管是可变对象和不可变对象进行copy操作,产生的都是不可变的对象,进行mutablecopy操作产生的都是可变的对象。

  • 对任何一个对象进行深拷贝,都是单层深拷贝。例如:对NSArray进行深拷贝,但是数组里面的元素还是指针拷贝。就是两个数组的地址不一样,但是里面的元素地址是一样的。所以要想让数组中的元素也是不一样的地址,那就对每个元素进行深拷贝。

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

推荐阅读更多精彩内容