当前位置: 移动技术网 > IT编程>移动开发>IOS > iOS 集合的深复制与浅复制解析

iOS 集合的深复制与浅复制解析

2018年10月12日  | 移动技术网IT编程  | 我要评论

坏丫头 音译,孙鸿志与杨安娣,反恐精英全球攻势中文版下载

ios 集合的深复制与浅复制解析

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

集合的浅复制 (shallow copy)

集合的浅复制有非常多种方法。当你进行浅复制时,会向原始的集合发送retain消息,引用计数加1,同时指针被拷贝到新的集合。
现在让我们看一些浅复制的例子:

nsarray *shallowcopyarray = [somearray copywithzone:nil];
nsset *shallowcopyset = [nsset mutablecopywithzone:nil];
nsdictionary *shallowcopydict = [[nsdictionary alloc] initwithdictionary:somedictionary copyitems:no];

集合的深复制 (deep copy)

集合的深复制有两种方法。

可以用 initwitharray:copyitems: 将第二个参数设置为yes即可深复制,如

nsdictionary shallowcopydict = [[nsdictionary alloc] initwithdictionary:somedictionary copyitems:yes];

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

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

nsarray *truedeepcopyarray = [nskeyedunarchiver unarchiveobjectwithdata:[nskeyedarchiver archiveddatawithrootobject:oldarray]];

集合的单层深复制

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

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

浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制。
深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制。
完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制。

不管是集合类对象,还是非集合类对象,接收到copy和mutablecopy消息时,都遵循以下准则:
copy返回imutable对象;所以,如果对copy返回值使用mutable对象接口就会crash;
mutablecopy返回mutable对象;
下面将针对非集合类对象和集合类对象的copy和mutablecopy方法进行具体的阐述

1、非集合类对象的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都是内容复制。用代码简单表示如下:

[immutableobject copy] // 浅复制
[immutableobject mutablecopy] //深复制
[mutableobject copy] //深复制
[mutableobject mutablecopy] //深复制

2、集合类对象的copy与mutablecopy
集合类对象是指nsarray、nsdictionary、nsset … 之类的对象。下面先看集合类immutable对象使用copy和mutablecopy的一个例子:

nsarray *array = @[@[@"a", @"b"], @[@"c", @"d"];
nsarray *copyarray = [array copy];
nsmutablearray *mcopyarray = [array mutablecopy];

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

nsmutablearray *array = [nsmutablearray arraywithobjects:[nsmutablestring stringwithstring:@"a"],@"b",@"c",nil];
nsarray *copyarray = [array copy];
nsmutablearray *mcopyarray = [array mutablecopy];

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

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

[immutableobject copy] // 浅复制
[immutableobject mutablecopy] //单层深复制
[mutableobject copy] //单层深复制
[mutableobject mutablecopy] //单层深复制

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

好了,深复制与浅复制就讲到这里。
最后说个题外的东西,在搜集资料的过程中,发现一个有可能犯错的点.

nsstring *str = @"string";
str = @"newstring";

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

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

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

p str 会打印对象本身的内存地址和对象内容

(lldb) p str
(nsstring *) $0 = 0x000000010c913680 @"a"

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

(lldb) po &str
0x00007fff532fb6c0

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

  • ios uicollectionview实现横向滚动

    现在使用卡片效果的app很多,之前公司让实现一种卡片效果,就写了一篇关于实现卡片的文章。文章最后附有demo实现上我选择了使用uicollectionview ... [阅读全文]
  • iOS UICollectionView实现横向滑动

    本文实例为大家分享了ios uicollectionview实现横向滑动的具体代码,供大家参考,具体内容如下uicollectionview的横向滚动,目前我使... [阅读全文]
  • iOS13适配深色模式(Dark Mode)的实现

    iOS13适配深色模式(Dark Mode)的实现

    好像大概也许是一年前, mac os系统发布了深色模式外观, 看着挺刺激, 时至今日用着也还挺爽的终于, 随着iphone11等新手机的发售, ios 13系统... [阅读全文]
  • ios 使用xcode11 新建项目工程的步骤详解

    ios 使用xcode11 新建项目工程的步骤详解

    xcode11新建项目工程,新增了scenedelegate这个类,转而将原appdelegate负责的对ui生命周期的处理担子接了过来。故此可以理解为:ios... [阅读全文]
  • iOS实现转盘效果

    本文实例为大家分享了ios实现转盘效果的具体代码,供大家参考,具体内容如下demo下载地址: ios转盘效果功能:实现了常用的ios转盘效果,轮盘抽奖效果的实现... [阅读全文]
  • iOS开发实现转盘功能

    本文实例为大家分享了ios实现转盘功能的具体代码,供大家参考,具体内容如下今天给同学们讲解一下一个转盘选号的功能,直接上代码直接看viewcontroller#... [阅读全文]
  • iOS实现轮盘动态效果

    本文实例为大家分享了ios实现轮盘动态效果的具体代码,供大家参考,具体内容如下一个常用的绘图,主要用来打分之类的动画,效果如下。主要是ios的绘图和动画,本来想... [阅读全文]
  • iOS实现九宫格连线手势解锁

    本文实例为大家分享了ios实现九宫格连线手势解锁的具体代码,供大家参考,具体内容如下demo下载地址:效果图:核心代码://// clockview.m// 手... [阅读全文]
  • iOS实现卡片堆叠效果

    本文实例为大家分享了ios实现卡片堆叠效果的具体代码,供大家参考,具体内容如下如图,这就是最终效果。去年安卓5.0发布的时候,当我看到安卓全新的material... [阅读全文]
  • iOS利用余弦函数实现卡片浏览工具

    iOS利用余弦函数实现卡片浏览工具

    本文实例为大家分享了ios利用余弦函数实现卡片浏览工具的具体代码,供大家参考,具体内容如下一、实现效果通过拖拽屏幕实现卡片移动,左右两侧的卡片随着拖动变小,中间... [阅读全文]
验证码:
移动技术网