最近修复一bug ,进而窥见了copyWithZone(NSZone *)zone
该bug 场景是这样的
A 界面展示 一些地址列表,每行对应一个addressModel. 选中行
进入 B界面 展示 三行地址详情,分别是别名(如,公司),地址名(如,中关村XX). 其他(。。。) 选中B界面 地址名(中关村XX)
进入 C 界面 使用TextFied 输入并 搜索地址,
A 进入 B 进入 C 都是在界面跳转时 传递了同一个 addressModel.
C界面 搜索并选中地址后 回到 B 界面中,自然显示最新的选中结果(修改了同一个mode,大多数情况下,我们实现传递对象OC 是 用指针,*obj。). 需要在B 界面中 保持地址信息(B 就是编辑地址的界面,如删除,修改),回到A 界面 显示最新的修改结果。
但是在B 中修改了 地址信息 没有保持 直接返回 A 界面。这时 A中列表没有刷新,但实际上A界面内存中 该行对应Model 已经修改为B 界面中没有保持 的结果。 重复进入B 界面 ,傻眼了,显示了没有保持的 错误地址信息。
该bug 有不同解决思路。但本质应该是这样的。 A 中只需要给B 一个拷贝的对象,B 去修改或 不修改 在没有保存 同步到服务器 之前。返回A 没有任何影响。(B 中仅仅是个 备份,及减少上下文 之间的依赖,也是copy 的本质作用)。
So,下面进入正题。如何 重写 copyWithZone(NSZone *)zone 实现 拷贝或 深拷贝。
首先,你要让自定义类实现NSCopying 或 NSMutableCopying 对应的是可变对象 和 非可变对象copy 协议。
如
CustomMode:NSObject<NSCopying>{
NSString * a;
int c;
}
……..
如果你的父类没有实现<NSCopying>,重写
-(id)copyWithZone(NSZone *)zone{
CustomMode *custom = [[[self class] copyWithZone:zone] init];
Custom ->_a = [_a copyWithZone:zone];
Custom -> _c = _c;//不是对象的 直接赋值
Return custom;
}实现深拷贝。在ARC 下。
如果你的父类实现了<NSCopying>,并重写了
-(id)copyWithZone(NSZone *)zone
CustomSuper:NSObject<NSCoping>
-(id)copyWithZone(NSZone *)zone{
CustomSuper *custom = [[[self class] copyWithZone:zone] init];
Return custom;
}
CustomMode:CustomSuper
-(id)copyWithZone(NSZone *)zone{
CustomModel *custom = [super copyWithZone:zone];
….
Return custom;
}
否则 直接向Custom 对象发copy 消息会蹦。
以为大功告成,但撇了一眼这篇文章 就不淡定了。
http://robnapier.net/implementing-nscopying
结论是。Objc_object 是一个结构体的实例。我们可以让所有的实例变量的快速内存拷贝为当前类。该结构的其余部分可能已被初始化为NULL如常。如果这听起来很复杂,应该是简单,只需调用class_getInstanceSize()为你的父类。添加它为self 去得到你第一个实例偏移。和memcpy()为您class_getInstanceSize()减去你的超类的字节数。然后,你可以清理你自己的保留计数不搞砸了你的子类。更妙的是,多个子类可以使用这个快速复制,而不会影响对方,相比NSCopyObject(),它只能通过顶层类的使用。
比如父类中实现 子类的深拷贝:
- (id)copyWithZone:(NSZone *)zone { id copyInstance = [[[self class] allocWithZone:zone] init]; size_t instanceSize = class_getInstanceSize([self class]); memcpy((__bridge voidvoid *)(copyInstance), (__bridge const voidvoid *)(self), instanceSize); return copyInstance; }
延伸Copy 和 metableCopy 详解:
http://www.cnblogs.com/ydhliphonedev/archive/2012/04/27/2473927.html
简单说就是 copy 返回不可变对象,metableCopy 返回可变对象。但 在程序中
对于 不可变对象(如:常量 。NSString *a = @"abc".)copy 返回不可变对象,常量本来就是不可变的。如果重新分配内存,有点浪费啦。
所有 copy 后的对象 和a 都指向 内存中的一块保存常量的内存块。 及浅拷贝,此时指向常量的地址 *a 引用计数为2。metableCopy 返回可变对象,所有会赋值内容。及深拷贝。
不可变对象(如:常量)copy 是浅拷贝, metableCopy 是深拷贝。
对于可变对象 copy 返回不可变对象, 所以会拷贝内容,其内容不可变。 metableCopy 也会拷贝内容,但内容是可变的。 都是深拷贝。
可变对象 copy 和 metableCopy 都是 深拷贝。
关于容器实现copy 或 metableCopy ,容器内元素默认都是 指针拷贝,
除非 使用
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject: array]];
实现元素的 内容拷贝 ,深拷贝。
可变容器和不可变容器 本事也是遵循 上面结论的。这里会有不可变容器内 有可变元素 不可变元素 共存的情况,可变容器也一样。
NSArray *mArray1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
copy 和metableCopy 对容器本事是深拷贝。起内部元素是指针拷贝。
参见:
实现NSCopying(或NSCopyObject()是有害的)
http://robnapier.net/implementing-nscopying
http://blog.csdn.net/garychow520/article/details/20548383
http://stackoverflow.com/questions/12572999/what-is-the-operator-doing-in-copywithzone?rq=1
http://stackoverflow.com/questions/4089238/implementing-nscopying