(0056)iOS开发之深拷贝与浅拷贝
参考:
http://blog.csdn.net/qq_17007915/article/details/50503043
http://www.cnblogs.com/ludashi/p/3894151.html
iOS开发之深拷贝与浅拷贝
打个比方,对于同一个抽屉里的蛋糕,深拷贝时制作一份新的蛋糕存起来,浅拷贝是复制一把打开抽屉钥匙存起来。
深拷贝拷贝的是内容,浅拷贝拷贝的是指针。用一句简单的话来说就是浅拷贝,是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
理解一:
浅拷贝:是拷贝操作后,生成了另一个指针也指向了同一个地址。
深拷贝:拷贝操作后,是真正的复制了一份,另一个指针指向了,拷贝后的地址。如下图:A代表原有的指针,B代表拷贝的指针。
--------浅拷贝----------------------深拷贝------
从上图中可以看到,浅拷贝(浅复制)中如果其中A指针改变了所指向的地址的内容,那么B指针也指向被修改后的内容。如果有些地方用到B指针,即便A指向的内容发生变化,也不希望B受到影响,则需要用深拷贝,真正复制一份A指向的内容,B指向复制后的值,这样即使A指向的内容变化了,B也不会产生影响。网上也有人通俗理解为:浅复制好比你和你的影子,你完蛋,你的影子也完蛋。深复制好比你和你的克隆人,你完蛋,你的克隆人还活着。
理解二:
深拷贝和浅拷贝的本质是地址相同,就是浅拷贝,地址不同就是深拷贝。
ios开发过程中,大体上会区分为对象和容器两个概念,对象的copy是浅拷贝,mutablecopy是深拷贝。容器也参照如上方法,但是需要记住,容器的包含对象的拷贝,无论使用copy,还是mutablecopy都将是浅拷贝。要想实现对象的深拷贝,必须自己提供拷贝的方法。自己提供的方法见下面的注意点。
三. 理解三(代码方式)
NSArray *array = [NSArray arrayWithObjects:@"one",@"two", nil];
NSMutableArray *array1 = [array copy];
[array1 addObject:@"three"];
// 这段代码是错误的。array1,通过copy进行的是浅拷贝,即并没有真正复制array,而是也指向了array,此时array是不可变数组,无法进行新数据的添加
NSArray *array=[NSArray arrayWithObjects:@"one",@"two", nil];
NSMutableArray *array2=[array mutableCopy];
[array2 addObject:@"three"];
// 这段代码是正确的,array2通过mutableCopy进行的是深拷贝,即把array真正复制了一份,并且复制后,变为了NSMutableArray ,此时array2是可变数组,可以添加数据。
注意点:(1)当使用mutableCopy时,不管源对象是否可变,副本是可变的,并且实现真正意义上的拷贝。
当我们使用copy一个可变对象时,副本对象是不可变的。
(2)要想实现对象的自定义拷贝,必须实现NSCopying,NSMutableCopying协议,实现该协议的copyWithZone方法和mutableCopyWithZone方法。深拷贝和浅拷贝的区别就在于copyWithZone方法的实现。
UI是iOS的UI库,用objective-c封装的,一般用于普通的视图和控制器,如UIView、UIImageView、UITableViewController等。
NS是objc的基础库
CG,CF等是比较底层的C语言的库
可以理解UIFont就是用objc封装过的CGFont,用起来方便些
1. 非容器不可变对象,比如NSString
2. 非容器可变对象:比如NSMutableString
3. 容器类不可变对象: 比如NSArray
4. 容器类可变对象: 比如NSMutableArray
NSLog(@“打印对象引用计数:%ld",CFGetRetainCount((__bridgeCFTypeRef)(arrayCopy1)));
NSLog(@"打印指针内存地址:%x",&ggg);
NSLog(@"打印指针所指向对象的地址:%p",ggg);
1.非容器 + 不可变对象 + retain + copy + mutableCopy
NSLog(@"非容器类不可变对象拷贝NSString");
NSString*str =@"ludashi";
// retain
NSString*str1 =[strretain];
// copy
NSString*str2 =[strcopy];
// mutableCopy
NSString*str3 =[strmutableCopy];
// 分别输出指针内存地址,指针所指向对象的地址,对象引用计数
NSLog(@"str指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d”,&str,str,str.retainCount);
NSLog(@"str1指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&str1,str1,str1.retainCount);
NSLog(@"str2指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&str2,str2,str2.retainCount);
NSLog(@"str2指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&str3,str3,str3.retainCount);
2.非容器 + 可变对象 + retain + copy + mutableCopy
NSLog(@"非容器类的可变对象拷贝");
NSMutableString*s =[NSMutableString stringWithFormat:@"ludashi"];
//把s通过retain的方式把值 赋给s1;
NSMutableString*s1 =[sretain];
//把s通过copy的方式把值赋给s2;
NSMutableString*s2 =[scopy];
//把s通过mutableCopy的方式把值赋给s3
NSMutableString*s3 =[smutableCopy];
NSLog(@"s指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d”,&str,str,str.retainCount);
NSLog(@"s1指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&str1,str1,str1.retainCount);
NSLog(@"s2指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&str2,str2,str2.retainCount);
NSLog(@"s2指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&str3,str3,str3.retainCount);
运行结果分析:
1.retian对对可变对象为浅拷贝
2.copy对可变对象非容器类为深拷贝
3.mutableCopy对可变非容器类为深拷贝
3.容器类 + 非可变对象 + retain + copy + mutableCopy
下面对容器类的非可变对象进行测试,有程序的运行结果可知当使用mutableCopy时确实返回了一个新的容器(由内存地址可以看出),但从容器对象看而言是容器的深拷贝,但从输出容器中的元素是容器的浅拷贝。
NSMutableString *string = [NSMutableString stringWithFormat:@"ludashi"];
//第二种:容器类不可变对象拷贝
NSLog(@"容器类不可变对象拷贝");
NSArray *array = [NSArray arrayWithObjects:string,@"b", nil];
//把array通过retain方式把值赋给array1
NSArray *array1 = [array retain];
//把array通过copy的方式把值赋给array2
NSArray *array2 = [array copy];
//把array通过mutableCopy方式把值赋给array3
NSArray *array3 = [array mutableCopy];
NSLog(@"array指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d”,&array,array,array.retainCount);
NSLog(@"array1指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&array1,array1,array1.retainCount);
NSLog(@"array2指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&array2,array2,array2.retainCount);
NSLog(@"array3指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&array3,array3,array3.retainCount);
//打印输出每个可变容器中元素的地址
NSLog(@"打印输出每个可变容器中元素的地址");
NSLog(@" array[0] = %p", array[0]);
NSLog(@"array1[0] = %p", array1[0]);
NSLog(@"array2[0] = %p", array2[0]);
NSLog(@"array3[0] = %p", array3[0]);
4.容器类 + 可变对象 + retain + copy + mutableCopy
面对容器类的可变对象进行测试,copy和mutableCopy对于容器本身是深拷贝,原因是返回了一个新的容器地址,但对于容器中的元素仍然是浅拷贝。
NSLog(@"********************************************\n\n\n\n");
//第四种:容器类的可变对象的拷贝,用NSMutableArray来实现
NSLog(@"容器类的可变对象的拷贝");
NSMutableArray *m_array = [NSMutableArray arrayWithObjects:string, nil];
//把m_array通过retain把值赋给m_array1
NSMutableArray *m_array1 = [m_array retain];
//把m_array通过copy把值赋给m_array2
NSMutableArray *m_array2 = [m_array copy];
//把m_array通过mytableCopy把值赋给m_array3
NSMutableArray *m_array3 = [m_array mutableCopy];
NSLog(@"m_array指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d”,&m_array,m_array,m_array.retainCount);
NSLog(@"m_array1指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&m_array1,m_array1,m_array1.retainCount);
NSLog(@"m_array2指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&m_array2,m_array2,m_array2.retainCount);
NSLog(@"m_array3指针内存地址:%x——:指针所指向对象的地址:%p——对象引用计数:%d",&m_array3,m_array3,m_array2.retainCount);
//打印输出每个可变容器中元素的地址
NSLog(@"打印输出每个可变容器中元素的地址");
NSLog(@" m_array[0] = %p", m_array[0]);
NSLog(@"m_array1[0] = %p", m_array1[0]);
NSLog(@"m_array2[0] = %p", m_array2[0]);
NSLog(@"m_array3[0] = %p", m_array3[0]);
上面的代码以及代码的运行结果翻来复去就是在验证下面的结论:
1.retain:始终是浅复制。引用计数每次加一。返回对象是否可变与被复制的对象保持一致。
2.copy:对于可变对象为深复制,引用计数不改变;对于不可变对象是浅复制, 引用计数每次加一。始终返回一个不可变对象。
3.mutableCopy:始终是深复制,引用计数不改变。始终返回一个可变对象。
第五、浅copy和深copy
浅复制尽复制对象本身,对象里的属性、包含的对象不做复制
深复制复制全部,包括对象的属性和其他对象
Foundation框架支持复制的类,默认是浅复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
NSMutableArray *array = [[NSMutableArray alloc] init]; for(int i=0;i<3;i++) { NSObject *obj = [[NSObject alloc] init]; [array addObject:obj]; [obj release]; } for(NSObject *obj1 in array) { NSLog(@"地址为 %p,引用计数是 %ld",obj1,obj1.retainCount); } NSMutableArray *array2=[array copy]; for(NSObject *obj2 in array2) { NSLog(@"地址为 %p,引用计数是 %ld",obj2,obj2.retainCount); } |
2013-09-3017:28:01.492 FDAS[681:303] 地址为 0x1001081f0,引用计数是 1 2013-09-3017:28:01.506 FDAS[681:303] 地址为 0x100108230,引用计数是 1 2013-09-3017:28:01.506 FDAS[681:303] 地址为 0x100108240,引用计数是 1 2013-09-3017:28:01.507 FDAS[681:303] 地址为 0x1001081f0,引用计数是 2 2013-09-3017:28:01.507 FDAS[681:303] 地址为 0x100108230,引用计数是 2 2013-09-3017:28:01.507 FDAS[681:303] 地址为 0x100108240,引用计数是 2 |
3. 自定义类对象之间的深浅拷贝问题
在Objective-C中并不是所有的类都支持拷贝;只有遵循NSCopying协议的类,才支持copy拷贝,只有遵循NSMutableCopying协议的类,才支持mutableCopy拷贝。如果没有遵循拷贝协议,拷贝时会出错。
如果我们想再我们自定义的类中支持copy和mutableCopy那么我们就需要使我们定义的类遵循NSCopying和NSMutableCopying协议,代码如下:
@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;
}
(0056)iOS开发之深拷贝与浅拷贝相关推荐
- IOS中的深拷贝和浅拷贝
标签: 什么是深拷贝?什么是浅拷贝? 为什么经常看到字符串属性要这样定义,那个copy是神马意思? @property(nonatomic,copy)NSString* name; 为什么下面的写法是 ...
- iOS开发——深拷贝与浅拷贝详解
深拷贝和浅拷贝这个问题在面试中常常被问到,而在实际开发中,只要稍有不慎,就会在这里出现问题.尤其对于初学者来说,我们有必要来好好研究下这个概念.我会以实际代码来演示,相关示例代码上传至 这里 . 首先 ...
- 深拷贝与浅拷贝(mutableCopy与Copy)详解 iOS
深拷贝与浅拷贝(mutableCopy与Copy)详解 iOS ios中并不是所有的对象都支持copy,mutableCopy,遵守NSCopying 协议的类可以发送copy消息,遵守NSMutab ...
- iOS开发几年了,你清楚OC中的这些东西么!!!?
iOS开发几年了,你清楚OC中的这些东西么!!!? 前言 几年前笔者是使用Objective-C进行iOS开发, 不过在两年前Apple发布swift的时候,就开始了swift的学习, 在swift1 ...
- iOS开发知识点总结
main文件做了这几件事:1. 创建当前的应用程序2. 根据4个参数的最后为应用程序设置代理类(默认情况下是AppDelegate)3. 将appDelegate 和 应用程序 建立关联(指定代理,) ...
- iOS开发笔记之八十一——2020 iOS面试总结《一》之干货篇
******阅读完此文,大概需要5分钟****** 这是我毕业之后第三次开始找工作了,适逢2019年底,我清楚地知道,iOS开发已经不是很景气了,尽管自己有名校以及大厂背景,但是自己一点都没有把握,自 ...
- 校园招聘iOS开发岗位面试题集锦(2017)
转发自: http://blog.csdn.net/chenyufeng1991/article/details/53472284#comments 一.搜狐快站 1.谈谈你做过的项目: 2.项目中 ...
- 2017秋季校园招聘iOS开发岗位面试题集锦
笔者参加了2017秋季不少的校招iOS岗位面试,下面我把这些面试题都贴出来和大家共勉: 一.搜狐快站 1.谈谈你做过的项目: 2.项目中最有成就感的部分: 3.倒计时如何实现?(NSTimer,还有其 ...
- 互联网公司iOS开发工程师面试必看(最全知识点梳理)
序言 目前形势,参加到iOS队伍的人是越来越多,甚至已经到供过于求了.今年,找过工作人可能会更深刻地体会到今年的就业形势不容乐观,加之,培训机构一火车地向用人单位输送iOS开发人员,打破了生态圈的动态 ...
最新文章
- Linux查看和剔除当前登录用户详细教程
- 安装Terminator和快捷键使用
- MongoDB数据库--扩展Base64,算法
- java创建日程到期提醒_Mac用户必备日程时间管理器,有计划有效率的人生才算完美!...
- P1803 凌乱的yyy / 线段覆盖
- Stark 组件:快速开发神器 —— 页面显示
- c语言exit_看了这几个C语言例子,你一定会说5个哇塞,声音一次比一次大
- “练好内功坚持被集成”,阿里云发布SaaS加速器
- IDEA解决maven多module出现多root的问题
- PyTorch学习—11.权值初始化
- java秒数格式转换_Java中整数(秒数)转换为时分秒格式(xx:xx:xx)
- 深度学习:Image Object Detection方法
- mysql 5.6 在线DDL
- 【matlab算法原理详解】车牌识别算法
- leetcode--打家劫舍
- 域名whois查询违规吗_WHOIS域ID隐私保护如何工作? 我需要域名WHOIS隐私吗?
- Linux下安装Adobe Flash Player插件(Firefox)
- date_histogram
- react 使用 swiper
- 计算机教师的应用计划书,教师信息技术个人提升计划