ObjectiveC 深浅拷贝学习
在ObjC中,什么是深浅拷贝?
深浅拷贝分别指深拷贝和浅拷贝,即mutableCopy和copy方法。
copy复制一个不可变对象,而mutableCopy复制一个mutable可变对象。
什么时候用到深浅拷贝?下面举几个例子说明。 本质确实是地址相同,就是浅拷贝,地址不同就是深拷贝。
非容器类对象
如NSString,NSNumber等一类对象
示例1:
1
2 3 4 5 |
// 非容器类对象
NSString *str = @"origin string"; NSString *strCopy = [str copy]; NSMutableString *mstrCopy = [str mutableCopy]; [mstrCopy appendString:@"??"]; |
查看内存可以发现,str和strCopy指向的是同一块内存区域,我们称之为弱引用(weak
reference)。而mstrCopy是真正的复制,系统为其分配了新内存空间,保存从str复制过来的字符串值。从最后一行代码中修改这些值而不影
响str和strCopy中可证明。
示例2:
1
2 3 4 5 6 7 |
NSMutableString *mstr = [NSMutableString stringWithString:@"origin"];
NSString *strCopy = [mstr copy]; NSMutableString *mstrCopy = [mstr copy]; NSMutableString *mstrMCopy = [mstr mutableCopy]; //[mstrCopy appendString:@"1111"]; //error [mstr appendString:@"222"]; [mstrMCopy appendString:@"333"]; |
以上四个对象所分配的内存都是不一样的。而且对于mstrCopy,它所指向的其实是一个imutable对象,是不可改变的,所以会出错。这点要注意,好好理解。
小结:
对于非容器类对象,有:
- 如果对一个不可变对象复制,copy是指针复制,即浅拷贝;而mutableCopy则是对象复制,即深拷贝。(示例1)
- 如果是对可变对象复制,都是深拷贝,但copy复制返回的对象是不可变的。(示例2)
容器类对象深浅复制
比如NSArray,NSDictionary等。对于容器类本身,上面讨论的结论也适用的,下面探讨的是复制后容器内对象的变化。
示例3
1
2 3 4 5 6 7 8 9 10 11 12 |
/* copy返回不可变对象,mutablecopy返回可变对象 */
NSArray *array1 = [NSArray arrayWithObjects:@"a",@"b",@"c",nil]; NSArray *arrayCopy1 = [array1 copy]; //arrayCopy1是和array同一个NSArray对象(指向相同的对象),包括array里面的元素也是指向相同的指针 NSLog(@"array1 retain count: %d",[array1 retainCount]); NSLog(@"array1 retain count: %d",[arrayCopy1 retainCount]); NSMutableArray *mArrayCopy1 = [array1 mutableCopy]; //mArrayCopy1是array1的可变副本,指向的对象和array1不同,但是其中的元素和array1中的元素指向的还是同一个对象。mArrayCopy1还可以修改自己的对象 [mArrayCopy1 addObject:@"de"]; [mArrayCopy1 removeObjectAtIndex:0]; |
array1和arrayCopy1是指针复制,而mArrayCopy1是对象复制,符合前面示例1讨论的结论。mArrayCopy1可以改变其内的元素:删除或添加。但容器内的元素内容都是浅拷贝。
示例4
1
2 3 4 5 6 7 8 9 10 11 12 13 |
NSArray *mArray1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]); NSArray *mArrayCopy2 = [mArray1 copy]; NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]); // mArray1和mArrayCopy2指向同一对象,retain值+1。 NSMutableArray *mArrayMCopy1 = [mArray1 mutableCopy]; NSLog(@"mArray1 retain count: %d",[mArray1 retainCount]); //mArrayCopy2和mArray1指向的是不一样的对象,但是其中的元素都是一样的对象——同一个指针 NSMutableString *testString = [mArray1 objectAtIndex:0]; |
由此可见,对于容器而言,其元素对象始终是指针复制。如果需要元素对象也是对象复制,就需要实现深拷贝。http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Collections/Articles/Copying.html
示例5
1
2 3 4 |
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"],[NSStringstringWithString:@"b"],@"c",nil];
NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES]; NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject: array]]; |
trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则不是,对于deepCopyArray内的不可变元素其还是指针复制。
或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。
举个例子,[[array objectAtIndex:0]appendstring:@”sd”]后其他的容器内对象并不会受影响。[[array
objectAtIndex:1]和[[deepCopyArray
objectAtIndex:0]尽管是指向同一块内存,但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全
意义上的深拷贝。
自己实现深拷贝的方法
NSDictionaryMutableDeepCopy.h
1
2 3 4 5 6 7 8 |
#import <foundation /Foundation.h>
@interface NSDictionary(MutableDeepCopy) - (NSMutableDictionary *)mutableDeepCopy; @end |
NSDictionaryMutableDeepCopy.m
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#import "NSDictionaryMutableDeepCopy.h"
@implementation NSDictionary(MutableDeepCopy) - (NSMutableDictionary *)mutableDeepCopy { @end |
使用类别方法来实现。
自定义对象
如果是我们定义的对象,那么我们自己要实现NSCopying,NSMutableCopying这样就能调用copy和mutablecopy了。举个例子:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 5 |
@interface MyObj : NSObject<nscopying ,NSMutableCopying>
{ NSMutableString *name; NSString *imutableStr; int age; } @property (nonatomic, retain) NSMutableString *name; @property (nonatomic, retain) NSString *imutableStr; @property (nonatomic) int age; @end @implementation MyObj @synthesize name; @synthesize age; @synthesize imutableStr; - (id)init { if (self = [super init]) { self.name = [[NSMutableString alloc]init]; self.imutableStr = [[NSString alloc]init]; age = -1; } return self; } - (void)dealloc { [name release]; [imutableStr release]; [super dealloc]; } - (id)copyWithZone:(NSZone *)zone { MyObj *copy = [[[self class] allocWithZone:zone] init]; copy->name = [name copy]; copy->imutableStr = [imutableStr copy]; // copy->name = [name copyWithZone:zone];; // copy->imutableStr = [name copyWithZone:zone];// copy->age = age; return copy; } - (id)mutableCopyWithZone:(NSZone *)zone { MyObj *copy = NSCopyObject(self, 0, zone); copy->name = [self.name mutableCopy]; copy->age = age; return copy; } @end </nscopying |
ObjectiveC 深浅拷贝学习的更多相关文章
- ios学习路线—Objective-C(深浅拷贝)
在ObjC中,什么是深浅拷贝? 深浅拷贝分别指深拷贝和浅拷贝,即 mutableCopy 和 copy 方法. copy复制一个不可变对象,而 mutableCopy 复制一个 mutable 可 ...
- Python学习笔记——基础篇2【第三周】——计数器、有序字典、元组、单(双)向队列、深浅拷贝、函数、装饰器
目录 1.Python计数器Counter 2.Python有序字典OrderredDict 3.Python默认字典default 4.python可命名元组namedtuple 5.Python双 ...
- Python学习日记(五)——初识函数(set、深浅拷贝、三目运算、函数、全局变量和局部变量)
基本数据类型补充 set set集合,是一个无序且不重复的元素集合 #创建 s = {11,22,33,44}#类似字典 s = set() #转换 l = (11,22,33,44) s1 = se ...
- python 学习笔记5(深浅拷贝与集合)
拷贝 我们已经详细了解了变量赋值的过程.对于复杂的数据结构来说,赋值就等于完全共享了资源,一个值的改变会完全被另一个值共享. 然而有的时候,我们偏偏需要将一份数据的原始内容保留一份,再去处理数据,这个 ...
- 学习--->更新集合/内存/深浅拷贝
一.计算机基础 1..软件(应用程序) 2.解释器/编译器 - 解释型语言:将代码每一行传递给计算机一行,常用编程语言python,PHP,Ruby. - 编译型语言:将代码完全执行完后会形成一个文 ...
- python学习Day6 元组、字典、集合set三类数据用法、深浅拷贝
一.深浅拷贝 1. 值拷贝 ls1 = ls2 不开辟空间,指针跟着走.(直接将ls1中存放的地址拿过来,内存中不会开辟新的空间,所以你怎么变,我也跟着变.)(ls1内部的所有类型的值发生改变,l ...
- python学习笔记:深浅拷贝的使用和原理
在理解深浅拷贝之前,我们先熟悉下变量对象和数据类型 1.变量和对象 变量-引用-对象(可变对象,不可变对象) 在python中一切都是对象,比如[1,2],'hello world',123,{'k1 ...
- Python学习基础(二)——集合 深浅拷贝 函数
集合 # 集合 ''' 集合是无序不重复的 ''' # 创建列表 l = list((1, 1, 1)) l1 = [1, 1, 1] print(l) print(l1) print("* ...
- python学习打卡 day07 set集合,深浅拷贝以及部分知识点补充
本节的主要内容: 基础数据类型补充 set集合 深浅拷贝 主要内容: 一.基础数据类型补充 字符串: li = ["李嘉诚", "麻花藤", "⻩海峰 ...
随机推荐
- Java核心技术(Java白皮书)卷Ⅰ 第一章 Java程序设计概述
第1章 Java程序设计概述1.1 Java程序设计平台 具有令人赏心悦目的语法和易于理解的语言,与其他许多优秀语言一样,Java满足这些要求. 可移植性 垃圾收集 提供大型的库 如果想要有奇特的绘 ...
- ASP.NET MVC框架开发系列教程
本系列教程是自己在工作中使用到而记录的,如有错误之处,请给与指正 文章目录 MVC4 开篇 第一章 初识MVC4 第二章 下山遇虎(@helper) 第三章 Models模块属性详解 第四章 在MVC ...
- 简单MVC理解与实现
MVC基本概念 MVC大家不陌生,包含模型(Model).视图(View).控制器(Controller),其中模型用于基本业务逻辑的实现,视图用于响应结果的表示,控制器用于模型控制和请求分派.先放上 ...
- python3之File文件方法
1.读写文件 open()将会返回一个file对象,基本语法: open(filename,mode) filename:是一个包含了访问的文件名称的路径字符串 mode:决定了打开文件的模式:只读, ...
- Windows程序设计学习笔记(四)自绘控件与贴图的实现
Windows系统提供大量的控件供我们使用,但是系统提供的控件样式都是统一的,不管什么东西看久了自然会厌烦,为了使界面更加美观,添加一些新的东西我们需要自己绘制控件. 控件在默认情况下并不进行自绘,如 ...
- CSS中垂直居中的方法
昨天总结了css中水平居中的方法,今天来总结一下css中实现垂直居中的方法. line-height line-height用于实现单行文本的垂直居中,如下图中,我们要求单行文本垂直居中,只需要将di ...
- IdentityServer Topics(1)- 启动说明
启动 IdentityServer的启动是中间件和服务的组合来实现的. 所有配置都在你的启动类(Startup.cs)中完成. 配置服务 通过以下方式调用将IdentityServer服务添加到DI系 ...
- js网页返回顶部和楼层跳跃的实现原理
这是简单的效果图. (实现楼层间的跳跃,主要依靠的是 window.scrollTo(x,y)方法 ,将浏览器的可见区域移动到指定的x,y坐标上.) 说楼层跳跃前,先温习下,一般网页在高度较大时, ...
- Scrapy运行错误:ImportError: No module named win32api
需要安装pypiwin32,直接通过官网安装exe会出现很多错误,所以直接运行以下命令: pip install pypiwin32
- ubuntu下查看-卸载软件(卸载.net core sdk的方法)
查看已安装的包:dpkg --list 查看正则匹配的包:dpkg --list 'dotnet-*' //查看以dotnet-开头的包 卸载匹配的包:sudo apt-get --purge rem ...