Как скопировать объект в Objective-C - PullRequest
109 голосов
/ 22 сентября 2009

Мне нужно глубоко скопировать пользовательский объект, который имеет свои собственные объекты. Я читал вокруг и немного запутался, как наследовать NSCopying и как использовать NSCopyObject.

Ответы [ 5 ]

188 голосов
/ 22 сентября 2009

Как всегда с ссылочными типами, есть два понятия «копия». Я уверен, что вы их знаете, но для полноты.

  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;
}
23 голосов
/ 16 декабря 2011

Документация Apple гласит

Версия подкласса copyWithZone: метод должен отправить сообщение супер сначала, чтобы включить его реализацию, если подкласс происходит непосредственно от 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;
}
21 голосов
/ 11 апреля 2014

Я не знаю разницы между этим кодом и моим, но у меня есть проблемы с этим решением, поэтому я прочитал немного больше и обнаружил, что мы должны установить объект, прежде чем возвращать его. Я имею в виду что-то вроде:

#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;
}

Я добавил этот ответ, потому что у меня много проблем с этим вопросом, и я понятия не имею, почему это происходит. Я не знаю разницы, но это работает для меня, и, возможно, это может быть полезно и для других:)

2 голосов
/ 24 июля 2012
another.obj = [obj copyWithZone: zone];

Я думаю, что эта строка вызывает утечку памяти, потому что вы получаете доступ к obj через свойство, которое (я предполагаю) объявлено как retain. Таким образом, оставшийся счет будет увеличен за счет свойства и copyWithZone.

Я считаю, что это должно быть:

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

или

SomeOtherObject *temp = [obj copyWithZone: zone];
another.obj = temp;
[temp release]; 
0 голосов
/ 03 ноября 2017

Существует также использование оператора -> для копирования. Например:

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

Причина в том, что результирующий скопированный объект должен отражать состояние исходного объекта. "." оператор может вводить побочные эффекты, так как этот вызывает геттеры, которые, в свою очередь, могут содержать логику.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...