iOS 集合的深复制与浅复制

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

概念

对象拷贝有两种方式:浅复制和深复制。顾名思义,浅复制,并不拷贝对象本身,仅仅是拷贝指向对象的指针;深复制是直接拷贝整个对象内存到另一块内存中。

一图以蔽之

image_note50592_1.png

再简单些说:浅复制就是指针拷贝;深复制就是内容拷贝。

________________________________________

集合的浅复制(shallow copy)

集合的浅复制有非常多种方法。当你进行浅复制时,会向原始的集合发送retain消息,引用计数加1,同时指针被拷贝到新的集合。

现在让我们看一些浅复制的例子:

1

2

3

4

5

6

1.NSArray*shallowCopyArray=[someArray copyWithZone:nil];

2.

3.NSSet*shallowCopySet=[NSSet mutableCopyWithZone:nil];

4.

5.NSDictionary*shallowCopyDict=[[NSDictionary alloc]initWithDictionary:someDictionary copyItems:NO];

________________________________________

集合的深复制(deep copy)

集合的深复制有两种方法。可以用initWithArray:copyItems:将第二个参数设置为YES即可深复制,如

1

1.NSDictionary shallowCopyDict=[[NSDictionary alloc] initWithDictionary:someDictionary copyItems:YES];

如果你用这种方法深复制,集合里的每个对象都会收到copyWithZone:消息。如果集合里的对象遵循NSCopying协议,那么对象就会被深复制到新的集合。如果对象没有遵循NSCopying协议,而尝试用这种方法进行深复制,会在运行时出错。copyWithZone:这种拷贝方式只能够提供一层内存拷贝(one-level-deepcopy),而非真正的深复制。

第二个方法是将集合进行归档(archive),然后解档(unarchive),如:

1

1.NSArray*trueDeepCopyArray=[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

________________________________________

集合的单层深复制(one-level-deep copy)

看到这里,有同学会问:如果在多层数组中,对第一层进行内容拷贝,其它层进行指针拷贝,这种情况是属于深复制,还是浅复制?对此,苹果官网文档有这样一句话描述

This kind of copy is only capable of producing a one-level-deep copy. If you only need a one-level-deep copy...

If you need a true deep copy, such as when you have an array of arrays...

从文中可以看出,苹果认为这种复制不是真正的深复制,而是将其称为单层深复制(one-level-deepcopy)。因此,网上有人对浅复制、深复制、单层深复制做了概念区分。

  • 浅复制(shallowcopy):在浅复制操作时,对于被复制对象的每一层都是指针复制。

  • 深复制(one-level-deepcopy):在深复制操作时,对于被复制对象,至少有一层是深复制。

  • 完全复制(real-deepcopy):在完全复制操作时,对于被复制对象的每一层都是对象复制。

当然,这些都是概念性的东西,没有必要纠结于此。只要知道进行拷贝操作时,被拷贝的是指针还是内容即可。

________________________________________

系统对象的copy与mutableCopy方法

不管是集合类对象,还是非集合类对象,接收到copy和mutableCopy消息时,都遵循以下准则:

  • copy返回imutable对象;所以,如果对copy返回值使用mutable对象接口就会crash;

  • mutableCopy返回mutable对象;

下面将针对非集合类对象和集合类对象的copy和mutableCopy方法进行具体的阐述

1、非集合类对象的copy与mutableCopy

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

1

2

3

1.NSString*string=@"origin";

2.NSString*stringCopy=[stringcopy];

3.NSMutableString*stringMCopy=[stringmutableCopy];

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

再看mutable对象拷贝例子

1

2

3

4

5

6

7

8

9

1.NSMutableString*string=[NSMutableStringstringWithString:@"origin"];

2.//copy

3.NSString*stringCopy=[stringcopy];

4.NSMutableString*mStringCopy=[stringcopy];

5.NSMutableString*stringMCopy=[stringmutableCopy];

6.//changevalue

7.[mStringCopyappendString:@"mm"];//crash

8.[stringappendString:@"origion!"];

9.[stringMCopyappendString:@"!!"];

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

综上两个例子,我们可以得出结论:

在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;对mutable对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:

  • [immutableObjectcopy]//浅复制

  • [immutableObjectmutableCopy]//深复制

  • [mutableObjectcopy]//深复制

  • [mutableObjectmutableCopy]//深复制

2、集合类对象的copy与mutableCopy

集合类对象是指NSArray、NSDictionary、NSSet...之类的对象。下面先看集合类immutable对象使用copy和mutableCopy的一个例子:

1

2

3

1.NSArray*array=@[ @[ @"a",@"b"],@[ @"c",@"d"];

2.NSArray*copyArray=[arraycopy];

3.NSMutableArray*mCopyArray=[arraymutableCopy];

查看内容,可以看到copyArray和array的地址是一样的,而mCopyArray和array的地址是不同的。说明copy操作进行了指针拷贝,mutableCopy进行了内容拷贝。但需要强调的是:此处的内容拷贝,仅仅是拷贝array这个对象,array集合内部的元素仍然是指针拷贝。这和上面的非集合immutable对象的拷贝还是挺相似的,那么mutable对象的拷贝会不会类似呢?我们继续往下,看mutable对象拷贝的例子:

1

2

3

1.NSMutableArray*array=[NSMutableArrayarrayWithObjects:[NSMutableStringstringWithString:@"a"],@"b",@"c",nil];

2.NSArray*copyArray=[arraycopy];

3.NSMutableArray*mCopyArray=[arraymutableCopy];

查看内存,如我们所料,copyArray、mCopyArray和array的内存地址都不一样,说明copyArray、mCopyArray都对array进行了内容拷贝。同样,我们可以得出结论:

在集合类对象中,对immutable对象进行copy,是指针复制,mutableCopy是内容复制;对mutable对象进行copy和mutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。用代码简单表示如下:

  • [immutableObjectcopy]//浅复制

  • [immutableObjectmutableCopy]//单层深复制

  • [mutableObjectcopy]//单层深复制

  • [mutableObjectmutableCopy]//单层深复制

这个代码结论和非集合类的非常相似。

这时候,是不是有人要问了,如果要对集合对象复制元素怎么办?有这疑问的同学不妨回头看看集合的深复制

好了,深复制与浅复制就讲到这里。

________________________________________

最后说个题外的东西,在搜集资料的过程中,发现一个有可能犯错的点

1

2

1.NSString*str=@"string";

2.str=@"newString";

上面这段代码,在执行第二行代码后,内存地址发生了变化。乍一看,有点意外。按照C语言的经验,初始化一个字符串之后,字符串的首地址就被确定下来,不管之后如何修改字符串内容,这个地址都不会改变。但此处第二行并不是对str指向的内存地址重新赋值,因为赋值操作符左边的str是一个指针,也就是说此处修改的是内存地址。

所以第二行应该这样理解:将@"newStirng"当做一个新的对象,将这段对象的内存地址赋值给str。

我有如下的两个方法查看内存地址

  • pstr会打印对象本身的内存地址和对象内容

1

2

1.(lldb)pstr

2.(NSString*)$0=0x000000010c913680@"a"

  • po&str则打印的是引用对象的指针所在的地址

1

2

1.(lldb)po&str

2.0x00007fff532fb6c0

转载于:https://my.oschina.net/u/2361492/blog/677111

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/294199.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux实时进程优先级rt,Linux实时性- PREEMPT_RT实时抢占实现

作者:Paul E. McKenney翻译整理:土豆丝624原文链接:概述:本篇文章主要讲Linux的实时包PREEMPT_RT 是如何实现的。PREEMPT_RT 的原理PREEMPT_RT包的关键点是要使非抢占式的内核代码量尽可能的少,同时为了提供抢占性而必…

Adb安装程序出现TimeOut错误

为什么80%的码农都做不了架构师?>>> 安装Apk过程中,出现如下错误: Failed to install on device ‘XXX′: timeout 原因时设备速度太卡,导致启动超时,解决办法:延长超时时间。 方法&#xff…

2021.NET大会日程首发!行程亮点全曝光!

{倒计时4天文末有福利→.NET机器人定制抱枕}2021年12月18日由中国各地技术社区共同发起举办、知名企业和开源组织联合协办的2021年中国.NET开发者大会即将盛大开幕▽2020/12/18-12/19主题:开源共建|开放创新|开发赋能形式:线上直播- 长按二维码免费领票 …

C++9

C++类和new、delete操作符 在C++中,你可以像定义变量一样来创建对象,如: Student stu; //对象已被实例化,已分配内存空间,可以使用了 stu.say(); //调用成员函数 这种情况下,系统会在栈区为对象分配内存。栈区是内存中的一块区域,由系统自动分配和释放,程序员无法操控…

一个技术人的知识管理方法论

http://www.cnblogs.com/me-sa/p/my_methodology.html转载于:https://www.cnblogs.com/liushiyong1/p/3556299.html

继Science发文后,Nature也发文评论曹雪涛“误用图片”调查结果

全世界只有3.14 % 的人关注了爆炸吧知识本文转自:募格学术2021年1月26日傍晚 Nature 网站以头条新闻的方式刊出了题为“著名中国免疫学家没有剽窃和学术造假”的新闻并配以曹雪涛的照片,该新闻大篇幅报道了科技部等多部门对于中国工程院院士曹雪涛的联合…

linux运行.pak文件,使用game-to-flatpak脚本将商业Linux游戏安装程序转换为Flatpak应用程序...

现在有一个新的脚本,它允许你将各种商业Linux游戏的安装程序转换成可以在各种GNU/Linux发行版上运行的Flatpak软件包。这是一个开源的shell脚本,由GNOME开发人员Bastien Nocera开发,它做了一件事,即自动将各种格式的商业Linux游戏…

Log4j 2漏洞(CVE-2021-44228)的快速响应

简介2021 年 12 月 9 日,在Log4j的 GitHub 上公开披露了一个影响多个版本的 Apache Log4j 2 实用程序的高严重性漏洞 CVE-2021-44228、CVSSv3 10.0 (https://logging.apache.org/log4j/2.x) 。该漏洞由阿里云安全团队的陈兆军(可能为音译)发现…

Xcode4.5编译ffmpeg成功,过程说明

最近项目要用音视频的通话,需要用ffmpeg来实现,但是ffmpeg在iOS平台上的编译有些问题。 1 下载代码 1.下载ffmpeg源代码 git clone git://source.ffmpeg.org/ffmpeg.git ffmpeg 参考:http://ffmpeg.org/download.html 2.下载ffmpeg-iphone…

何时使用[self release]

这样的语句 [self release];乍看上去让人很困惑。 从release方法本身的作用上来说,就是给self的引用技术减一,就像release对其他对象所做的一样。一般来说,唯一用到,也是最合适使用 [self release];这一写法的地方是在initXXX方法…

C++10

C++友元函数和友元类 一个类中可以有 public、protected、private 三种属性的成员,通过对象可以访问 public 成员,只有本类中的函数可以访问本类的 private 成员。现在,我们来补充介绍一个例外——友元(friend)。 fnend 的意思是朋友,或者说是好友,与好友的关系显然要比…

box2d 绘制圆

在让刚体听我的——ApplyForce、ApplyImpulse、SetLinearVelocity一节中,来自天地会的sxl001问道如何创建圆形的边界(Round Boundary),好吧,我用这个教程来回答他。 实际上Box2D中没有专门创建圆弧的API (b2CircleDef创建的是实体圆形不是圆弧…

linux浏览器不能播放音频文件夹,在html中插入音频文件在浏览器中播放音频文件的兼容性问题...

下面谈谈本人在html中插入音频文件,经过我的本地测试总结的一些问题(播放mp3文件):1、问题:IE8上正常(通过media player插件来播放)但在IE6和IE7上不会播放Firefox上要安装QuickTime插件才能播放Chrome通过将其转化成html5上的标签播放&#…

如何让 Timer 在特定时间点触发?

咨询区 Behrooz Karjoo我的应用程序需要做一个 事件触发 的功能,它需要每天定时执行,比如说当天的 16点,我现在的做法是使用一个 timer 按秒轮询判断当前是否为 16:00, 虽然可以玩得转,但我想能不能实现那种 16:00 自动…

深入剖析Android系统

深入剖析Android系统(基于Google发布的Jelly Bean原始代码,讲述Android系统的内部静态结构关系和内部运行机制,为你呈现原汁原味的Android代码分析大餐!) 杨长刚著 ISBN 978-7-121-19374-3 2013年1月出版 定价&#xf…

微博上的网红,为什么更能红得发紫?

“网红”是最近非常热潮的互联网现象。在微博上,各种各样的网红不仅活跃了粉丝群体(微博月活跃用户增至2.61亿),也为微博增添了互动热度(微博一季度微博日均视频播放量达4.7亿次,同比增长489%,比…

TotoiseSVN-小乌龟的使用方法总结

原文转自 http://www.cnblogs.com/xilentz/archive/2010/05/06/1728945.html 收藏起来用的时候比较方便 TotoiseSVN的基本使用方法在 项目管理实践教程一、工欲善其事,必先利其器【Basic Tools】中,我已经讲解了怎样安装TortoiseSVN。在上面的讲解中已经…

UNIX环境高级编程笔记

1.setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, len); SO_REUSEADDR套接口选项允许为以下四个不同的目的提供服务: 一.SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知的端口,即使以前建立的将该端口用作它们的本地端口的连接仍存在。 …