如何在Objective-C中复制对象


112

我需要深入复制具有自己对象的自定义对象。我一直在阅读,对于如何继承NSCopying和如何使用NSCopyObject感到有些困惑。


1
理解副本,mutableCopy和copyWithZone 的绝妙教程
horkavlna 2013年

Answers:


192

与引用类型一样,“复制”有两个概念。我确定您了解它们,但出于完整性考虑。

  1. 按位复制。在这里,我们只是逐位复制内存-这就是NSCopyObject所做的。几乎总是,这不是您想要的。对象具有内部状态,其他对象等,并且经常假设它们是唯一持有对该数据的引用的对象。按位复制打破了这一假设。
  2. 深入的逻辑副本。在此方法中,我们制作了对象的副本,但实际上并没有一点一点地做它-我们想要一个对象在所有意图和目的上都具有相同的行为,但是(不一定)是原始对象的内存相同的克隆- Objective C手册称此类对象与其原始对象“在功能上是独立的”。因为制作这些“智能”副本的机制因类而异,所以我们要求对象自己执行它们。这是NSCopying协议。

您想要后者。如果这是您自己的对象之一,则只需采用协议NSCopying并实现-(id)copyWithZone:(NSZone *)zone。您可以自由地做任何想做的事。尽管您的想法是您复制自己的真实副本并返回。您可以在所有字段上调用copyWithZone进行深拷贝。一个简单的例子是

@interface YourClass : NSObject <NSCopying> 
{
   SomeOtherObject *obj;
}

// In the implementation
-(id)copyWithZone:(NSZone *)zone
{
  // We'll ignore the zone for now
  YourClass *another = [[YourClass alloc] init];
  another.obj = [obj copyWithZone: zone];

  return another;
}

但是,您要让复制对象的接收者负责释放它!不是吗autorelease,或者我在这里想念什么?
bobobobo

30
@bobobobo:否,Objective-C内存管理的基本规则是:如果对象使用名称以“ alloc”或“ new”开头或包含“ copy”的方法创建,则您将拥有该对象的所有权。copyWithZone:满足此条件,因此它必须返回保留计数为+1的对象。
史蒂夫·

1
@Adam是否有理由使用,alloc而不是allocWithZone:因为传入了区域?
理查德

3
好吧,区域在基于现代OS X的运行时中实际上是未使用的(即,我认为它们实际上从未使用过)。但是,可以的allocWithZone
亚当·赖特


25

苹果文档说

copyWithZone:方法的子类版本应首先将消息发送给super,以合并其实现,除非该子类直接来自NSObject。

添加到现有答案

@interface YourClass : NSObject <NSCopying> 
{
   SomeOtherObject *obj;
}

// In the implementation
-(id)copyWithZone:(NSZone *)zone
{
  YourClass *another = [super copyWithZone:zone];
  another.obj = [obj copyWithZone: zone];

  return another;
}

2
由于YourClass直接从NSObject继承而来,所以我认为这里没有必要
Mike

2
好点,但是如果它是一个长的类层次结构,这是一个通用规则。
萨奇布·沙特

8
我收到一个错误:No visible @interface for 'NSObject' declares the selector 'copyWithZone:'。我猜只有在我们从其他实现的自定义类继承时才需copyWithZone
2013年

1
another.obj = [[obj copyWithZone:zone] autorelease]; 对于NSObject的所有子类。对于原始数据类型,只需分配它们-> another.someBOOL = self.someBOOL;
hariszaman 2014年

@Sam“ NSObject本身不支持NSCopying协议。子类必须支持该协议并实现copyWithZone:方法。copyWithZone:方法的子类版本应首先将消息发送给super,以合并其实现,除非子类直接下降来自NSObject。” developer.apple.com/documentation/objectivec/nsobject/...
s4mt6

21

我不知道该代码和我的代码之间的区别,但是该解决方案存在问题,因此我阅读了更多内容,发现必须在返回对象之前对其进行设置。我的意思是:

#import <Foundation/Foundation.h>

@interface YourObject : NSObject <NSCopying>

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *line;
@property (strong, nonatomic) NSMutableString *tags;
@property (strong, nonatomic) NSString *htmlSource;
@property (strong, nonatomic) NSMutableString *obj;

-(id) copyWithZone: (NSZone *) zone;

@end


@implementation YourObject


-(id) copyWithZone: (NSZone *) zone
{
    YourObject *copy = [[YourObject allocWithZone: zone] init];

    [copy setNombre: self.name];
    [copy setLinea: self.line];
    [copy setTags: self.tags];
    [copy setHtmlSource: self.htmlSource];

    return copy;
}

我添加了此答案,因为我在此问题上有很多问题,并且不知道为什么会这样。我不知道区别,但是它对我有用,也许对其他人也有用:)


3
another.obj = [obj copyWithZone: zone];

我认为,这行会导致内存泄漏,因为您访问了obj(我假设)声明为的穿透属性retain。因此,保留计数将随属性和的增加而增加copyWithZone

我相信应该是:

another.obj = [[obj copyWithZone: zone] autorelease];

要么:

SomeOtherObject *temp = [obj copyWithZone: zone];
another.obj = temp;
[temp release]; 

不,方法alloc,copy,mutableCopy,new应该返回非自动释放的对象。
kovpas 2012年

@kovpas,您确定您了解我的意思吗?我不是在谈论返回的对象,而是在谈论它的数据字段。
Szuwar_Jr 2012年

是的,我不好,抱歉。您能否以某种方式编辑您的答案,以便我可以删除减号?:))
kovpas 2012年

0

还可以使用->运算符进行复制。例如:

-(id)copyWithZone:(NSZone*)zone
{
    MYClass* copy = [MYClass new];
    copy->_property1 = self->_property1;
    ...
    copy->_propertyN = self->_propertyN;
    return copy;
}

此处的理由是,生成的复制对象应反映原始对象的状态。“。” 运算符可能会引入副作用,因为该函数调用吸气剂,而该吸气剂又可能包含逻辑。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.