关于copyWithZone 实现拷贝 及copy metableCopy

最近修复一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

 

你可能感兴趣的:(table)