obj-c编程12:复制对象
好吧,上一篇我怎么也没想到会写那么多字那么少的代码,希望这一篇不会如此哦。
言归正传,对象的复制分为浅复制和深复制,前者只是复制对象的引用,当原对象的内容发生变化时,复制对象的内容也会发生变化,毕竟他们都指向同一个对象啊!有人可能会说了,原对象如果是不可改变对象,不就可以放心的做浅复制了吗?因为不能修改它的值,也就不用担心复制对象的内容改变啦!还是不行哦!我们设想如下情况:
#import <Foundation/Foundation.h> #define msg(...) NSLog(__VA_ARGS__) int main(int argc,char *argv[]) { @autoreleasepool{ //NSMutableString *val = @"val0",*key = @"key0"; NSMutableString *val = [NSMutableString stringWithString:@"val0"],\ *key = [NSMutableString stringWithString:@"key0"]; NSDictionary *dict_old,*dict_new; dict_old = [[NSDictionary alloc] initWithObjectsAndKeys:val,key,@"val1",@"key1",\ @"val2",@"key2",nil]; //不要忘记最后的nil哦! msg(@"%@",dict_old); void (^block)(id key,id obj,BOOL *stop) = ^(id key,id obj,BOOL *stop){ msg(@"key[%@]:val[%@]",key,obj); *stop = NO; }; [dict_old enumerateKeysAndObjectsUsingBlock:block]; dict_new = [dict_old copy]; //浅copy [val insertString:@"_fixed" atIndex:[val length]]; [key insertString:@"_fixed" atIndex:[key length]]; //[val setString:@"fix_val"]; //[key setString:@"fix_key"]; //注意,2个dict的val0值都变了,但key0值都没变,说明字典对key是深拷贝的。 //另外可以看出即使是不可变对象dict_old也不能保证其元素一定是不可改变的, //如果不是,则仍然会发生浅拷贝后被改变的情况。 msg(@"%@\n**************************%@",dict_old,dict_new); } return 0; }
运行结果如下:
apple@kissAir: objc_src$clang -fobjc-arc -framework Foundation 5.m -o 5
apple@kissAir: objc_src$./5
2014-07-03 11:47:52.496 5[1447:507] {
key0 = val0;
key1 = val1;
key2 = val2;
}
2014-07-03 11:47:52.498 5[1447:507] key[key1]:val[val1]
2014-07-03 11:47:52.498 5[1447:507] key[key0]:val[val0]
2014-07-03 11:47:52.499 5[1447:507] key[key2]:val[val2]
2014-07-03 11:47:52.499 5[1447:507] {
key0 = "val0_fixed";
key1 = val1;
key2 = val2;
}
**************************{
key0 = "val0_fixed";
key1 = val1;
key2 = val2;
}
再看另外一种情况:
#import <Foundation/Foundation.h> #define msg(...) NSLog(__VA_ARGS__) #define mstr(x) [NSMutableString stringWithString:x] int main(int argc,char *argv[]) { @autoreleasepool{ NSMutableArray *m_ary = [NSMutableArray arrayWithObjects:\ mstr(@"one"),mstr(@"two"),mstr(@"three"),nil]; NSMutableArray *m_ary_new; NSMutableString *mstr; for(NSString *str in m_ary) msg(@" %@",str); m_ary_new = [m_ary mutableCopy]; mstr = [m_ary objectAtIndex:0]; [mstr appendString:@"_fixed"]; msg(@"%@\n****************************%@",m_ary,m_ary_new); } return 0; }
你能猜到运行结果吗?
apple@kissAir: objc_src$./5
2014-07-03 12:00:01.235 5[1494:507] one
2014-07-03 12:00:01.237 5[1494:507] two
2014-07-03 12:00:01.237 5[1494:507] three
apple@kissAir: objc_src$clang -fobjc-arc -framework Foundation 5.m -o 5
apple@kissAir: objc_src$./5
2014-07-03 12:02:17.727 5[1504:507] one
2014-07-03 12:02:17.729 5[1504:507] two
2014-07-03 12:02:17.730 5[1504:507] three
2014-07-03 12:02:17.731 5[1504:507] (
"one_fixed",
two,
three
)
****************************(
"one_fixed",
two,
three
)
为什么我改变m_ary的元素,m_ary_new同位置的元素值也会变化呢?其实对于浅拷贝来说m_ary每个元素只是拷贝其引用到新的m_ary_new的元素上,所以...没有所以了 :),那如果我要改变m_ary的元素同时m_ary_new不发生变化该怎么办呢?你可以创建一个新的元素喽:
mstr = [NSMutableString stringWithString:[m_ary objectAtIndex:0]]; [mstr appendString:@"fixed"]; [m_ary replaceObjectAtIndex:0 withObject:mstr];
但是对于单一的可变字符串对象,copy拷贝的是深拷贝,这个要注意下:
#import <Foundation/Foundation.h> #define msg(...) NSLog(__VA_ARGS__) #define mstr(x) [NSMutableString stringWithString:x] int main(int argc,char *argv[]) { @autoreleasepool{ NSMutableString *mstr0 = mstr(@"test_str"),*mstr1; mstr1 = [mstr0 copy]; [mstr0 appendFormat:@"_%d_%d",101,102]; msg(@"%@ and %@",mstr0,mstr1); } return 0; }
运行结果如下:
apple@kissAir: objc_src$./5
2014-07-03 13:34:28.805 5[1762:507] test_str_101_102 and test_str
F库类实现了名为copy和mutableCopy的方法,可以使用他们来创建对象的副本。通过实现一个符合<NSCopying>协议的方法来完成此任务。如果类要区分产生可变副本和不可变副本,还要根据<NSMutableCopying>协议实现一个方法啊。
我们想象一个例子:数组A的一个可变副本copy到数组B,无论数组A是否可变,如果将B中第一个元素删除,A会不会受影响?答案是:不会!因为浅拷贝拷贝了数组中的每个元素的空间,即每个元素本身是深拷贝,只是指向的对象是一样的(浅拷贝),所以各个数组的元素是不受影响的(除非它们指向的对象值发生了变化,就像前2个代码示例一样。)
对于我们自己的类,只有实现<NSCopying>协议才可以调用copy方法(废话嘛)。在实现该协议时,类必须实现copyWithZone方法来响应copy消息。如果还要区分不可变副本,还要根据<NSMutableCopying>协议实现mutableCopyWithZone方法哦。如果2个方法都实现这前者返回不可变对象后者返回可变对象。
@implementation Some_class <NSCopying> -(id)copyWithZone:(NSZone*)zone{ Some_class *new_obj = [[Some_class allocWithZone:zone] init]; //如果该类可能会被继承则上一句改为: //id new_obj = [[[self class] allocWithZone:zone] init]; [new_obj set_x:x set_y:y]; return new_obj; } @end
zone参数与不同的存储区有关,你可以在程序中分配并使用这些存储区。直接传递给allocWithZone即可。
如果编写了类的copyWithZone方法,而该类的超类也实现了<NSCopying>协议,则应该先调用超类的copy方法一复制继承来的实例变量,然后加入自己代码赋值新的实例变量(如果有的话)。你必须确定是否在类中实现浅拷贝或深拷贝,并编写文档,以告知类的其他使用者。
obj-c编程12:复制对象的更多相关文章
- Effective C++ -----条款12: 复制对象时勿忘其每一个成分
Copying函数应该确保复制“对象内的所有成员变量”及“所有base class成分”. 不要尝试以某个copying函数实现另一个copying函数.应该将共同机能放进第三个函数中,并由两个cop ...
- EC读书笔记系列之7:条款12 复制对象时勿忘其每一个成分
记住: ★copying函数应确保复制“对象内的所有成员变量”及“所有base class成分” ★不要尝试以某个copying函数实现另一个copying函数.应该将共同机能放进第三个函数中,并由两 ...
- Effective C++(12) 复制对象时要复制每一个成员
问题聚焦: 负责拷贝的两个操作:拷贝构造函数和重载赋值操作符. 一句话总结,确保被拷贝对象的所有成员变量都做一份拷贝. Demo void logCall(const std::string&am ...
- Effective C++ .12 复制对象-拷贝构造函数的编写
当我们自己编写拷贝构造函数时,编译器就不会为该类生成默认拷贝构造函数了,对于assignment operator也是如此. 1. 拷贝构造函数中记得调用父类的拷贝构造函数,或者相应复制过程 clas ...
- EC笔记:第二部分:12、复制对象时勿忘其每一个成分
EC笔记:第二部分:12.复制对象时勿忘其每一个成分 1.场景 某些时候,我们不想使用编译器提供的默认拷贝函数(包括拷贝构造函数和赋值运算符),考虑以下类定义: 代码1: class Point{ p ...
- Effective C++ 条款12:复制对象时勿忘其每一个成分
void logCall(const std::string& funcName); class Customer { public: ... Customer (const Customer ...
- Effective C++ 条款11,12 在operator= 中处理“自我赋值” || 复制对象时不要忘记每一个成分
1.潜在的自我赋值 a[i] = a[j]; *px = *py; 当两个对象来自同一个继承体系时,他们甚至不需要声明为相同类型就可能造成别名. 现在担心的问题是:假如指向同一个对象, ...
- 条款12:复制对象时勿忘其每一个成分(Copy all parts of an object)
NOTE: 1.Copying 函数应该确保复制“对象内的所有成员变量”及“所有base class成分”. 2.不要尝试以某个copying函数实现另一个copying函数.应该将共同机能放进第三个 ...
- 13_Python的面向对象编程-类class,对象object,实例instance
1.面向对象概述 1.类是用来描述对象的工具,把拥有相同属性和行为的对象分为一组 2.对象是由类实例化出来的一个具体的对象 属性: 对象拥有的名词,用变量表示 ...
随机推荐
- Spark技术内幕之任务调度:从SparkContext开始
SparkContext是开发Spark应用的入口,它负责和整个集群的交互,包括创建RDD,accumulators and broadcast variables.理解Spark的架构,需要从这个入 ...
- Android开发学习之路--百度地图之初体验
手机都有gps和网络,通过gps或者网络可以定位到自己,然后通过百度,腾讯啊之类的地图可以显示我们的地理位置.这里学习下百度地图的使用.首先就是要申请开发者了,这个详细就不多讲了.http://dev ...
- EBS密码加密研究
DECLARE v_password_1 VARCHAR2(240); v_password_2 VARCHAR2(240); v_password_3 VARCHAR2(240); ...
- Google Guava的5个鲜为人知的特性
译文出处: 花名有孚 原文出处:takipi.com Google Guava有哪些比较冷门但却又实用的特性呢? 它是最流行的开源库之一,你应该听过它的大名,它诞生的地方正是人们举办真正的魁地奇比 ...
- EBS Form个性化的工作原理
Form的个性化是EBS的比较好用的一个东西.好像很少人研究它的工作原理,都是直接用.最近有个同事问我问题,说他在个性化编写的代码无效果.解决之后,才发现,原来传说中的EBS的Form的个性化是这样子 ...
- 联发科安卓6.0项目的到来的第一个难题:tar的分包与并包
tar 分包压缩与合并 今天是个高兴的日子,迎来了新项目----联发科平板.但是遇到了难题,tar的分包压缩与合并居然在资料书上找不 到,于是我赶紧百度,找到了相关资料. 在工程目录下遇到了大量的gz ...
- foreach 内嵌的使用
foreach内部处理数据流的每条记录,进行关系操作,最后用generate返回数据给外部.但注意关系操作符不能作用于表达式,要将表达式提取成关系. foreach内部只支持distinct, fil ...
- 使用spine骨骼动画制作的libgdx游戏
(官网:www.libgdx.cn) Super Spineboy是一个使用Spine和libgdx开发的跨平台游戏(Windows,Mac,Linux),Spine是一个2D游戏动画工具.Super ...
- nginx 平滑升级
怎么能在不停止服务的情况下,平滑的升级nginx?下面告诉你答案,其实很简单 1.下载nginx新版本,并解压,进入解压的目录 2.你要执行旧版本的nginx -V来查看旧版本编译的时候,编译了什么模 ...
- PO订单审批拒绝API
DECLARE l_return_status VARCHAR2(1); l_return_code VARCHAR2(1000); l_exception_msg VARCHAR2(4000); l ...