iOS开发之深复制和浅复制

简介:

1、概述

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

如下图:

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

 

2、集合的浅复制 (shallow copy)

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

现在让我们看三个浅复制的例子:


 

?
1
2
3
4
NSArray *shallowCopyArray = [someArray copyWithZone:nil];
NSSet *shallowCopySet = [NSSet mutableCopyWithZone:nil];
NSDictionary *shallowCopyDict =
[[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];


2、集合的深复制 (deep copy)

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

方法一:

可以用 initWithArray:copyItems: 将第二个参数设置为YES即可深复制,如

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

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

 

方法二:

将集合进行归档(archive),然后解档(unarchive),如:

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

 

3、集合的单层深复制 (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-deep copy)。因此,网上有人对浅复制、深复制、单层深复制做了概念区分。

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

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

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

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

 

4、系统对象的copymutableCopy方法

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

copy返回imutable对象(不可变对象);所以,如果对copy返回值使用mutable对象接口就会crash

mutableCopy返回mutable对象;

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

1)非集合类对象的copymutableCopy

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

?
1
2
3
NSString *string = @ "origin" ;
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];

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

再看mutable对象拷贝例子:

?
1
2
3
4
5
6
7
8
9
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:@ "!!" ];

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

 

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

在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;对mutable对象进行copymutableCopy都是内容复制。

用代码简单表示如下:

?
1
2
3
4
[immutableObject copy]  // 浅复制
[immutableObject mutableCopy]  //深复制
[mutableObject copy]  //深复制
[mutableObject mutableCopy]  //深复制

 

2)集合类对象的copymutableCopy

集合类对象是指NSArrayNSDictionaryNSSet ... 之类的对象。下面先看集合类immutable对象使用copymutableCopy的一个例子:

?
1
2
3
NSArray *array = @[@[@ "a" , @ "b" ], @[@ "c" , @ "d" ];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];

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

 

?
1
2
3
4
NSMutableArray *array =
[NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@ "a" ],@ "b" ,@ "c" ,nil];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];

查看内存,如我们所料,copyArraymCopyArrayarray的内存地址都不一样,说明copyArraymCopyArray都对array进行了内容拷贝。

 

同样,我们可以得出结论:

在集合类对象中,对immutable对象进行copy,是指针复制,mutableCopy是内容复制;对mutable对象进行copymutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。

用代码简单表示如下:

?
1
2
3
4
[immutableObject copy]  // 浅复制
[immutableObject mutableCopy]  //单层深复制
[mutableObject copy]  //单层深复制
[mutableObject mutableCopy]  //单层深复制

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

 

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

?
1
2
NSString *str = @ "string" ;
str = @ "newString" ;

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

 

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

 

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

方法一p str (会打印对象本身的内存地址和对象内容)

?
1
2
(lldb) p str
(NSString *) $ 0  0x000000010c913680  @ "a"

 

方法二:po &str (打印的是引用对象的指针所在的地址)

?
1
2
(lldb) po &str
0x00007fff532fb6c0
目录
相关文章
|
1月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
10天前
|
iOS开发 开发者 MacOS
深入探索iOS开发中的SwiftUI框架
【10月更文挑战第21天】 本文将带领读者深入了解Apple最新推出的SwiftUI框架,这一革命性的用户界面构建工具为iOS开发者提供了一种声明式、高效且直观的方式来创建复杂的用户界面。通过分析SwiftUI的核心概念、主要特性以及在实际项目中的应用示例,我们将展示如何利用SwiftUI简化UI代码,提高开发效率,并保持应用程序的高性能和响应性。无论你是iOS开发的新手还是有经验的开发者,本文都将为你提供宝贵的见解和实用的指导。
95 66
|
20天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
24天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
26天前
|
存储 前端开发 Swift
探索iOS开发:从新手到专家的旅程
本文将带您领略iOS开发的奇妙之旅,从基础概念的理解到高级技巧的掌握,逐步深入iOS的世界。文章不仅分享技术知识,还鼓励读者在编程之路上保持好奇心和创新精神,实现个人成长与技术突破。
|
29天前
|
安全 IDE Swift
探索iOS开发之旅:从初学者到专家
在这篇文章中,我们将一起踏上iOS开发的旅程,从基础概念的理解到深入掌握核心技术。无论你是编程新手还是希望提升技能的开发者,这里都有你需要的指南和启示。我们将通过实际案例和代码示例,展示如何构建一个功能齐全的iOS应用。准备好了吗?让我们一起开始吧!
|
1月前
|
安全 Swift iOS开发
Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法
本文深入探讨了 Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法。Swift 以其简洁、高效和类型安全的特点,结合 UIKit 丰富的组件和功能,为开发者提供了强大的工具。文章从 Swift 的语法优势、类型安全、编程模型以及与 UIKit 的集成,到 UIKit 的主要组件和功能,再到构建界面的实践技巧和实际案例分析,全面介绍了如何利用这些技术创建高质量的用户界面。
33 2
|
1月前
|
vr&ar Android开发 iOS开发
安卓与iOS开发中的用户界面设计原则
【10月更文挑战第41天】探索移动应用开发的精髓,本文将深入分析安卓和iOS平台上用户界面设计的核心原则。通过比较两大操作系统的设计哲学,我们将揭示如何打造直观、易用且美观的应用程序界面。无论你是初学者还是资深开发者,这篇文章都将为你提供宝贵的见解和实用的技巧,帮助你在竞争激烈的应用市场中脱颖而出。
|
1月前
|
设计模式 Swift iOS开发
探索iOS开发:从基础到高级,打造你的第一款App
【10月更文挑战第40天】在这个数字时代,掌握移动应用开发已成为许多技术爱好者的梦想。本文将带你走进iOS开发的世界,从最基础的概念出发,逐步深入到高级功能实现,最终指导你完成自己的第一款App。无论你是编程新手还是有志于扩展技能的开发者,这篇文章都将为你提供一条清晰的学习路径。让我们一起开始这段旅程吧!
|
1月前
|
iOS开发 开发者
探索iOS开发中的SwiftUI框架
【10月更文挑战第39天】在苹果的生态系统中,SwiftUI框架以其声明式语法和易用性成为开发者的新宠。本文将深入SwiftUI的核心概念,通过实际案例展示如何利用这一框架快速构建用户界面,并探讨其对iOS应用开发流程的影响。