Как скопировать объект в Objective-C

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

Ответ 1

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

  • Побитовая копия. В этом случае мы просто копируем бит памяти для бит - это то, что делает NSCopyObject. Почти всегда, это не то, что вы хотите. Объекты имеют внутреннее состояние, другие объекты и т.д., И часто делают предположения, что они являются единственными, у которых есть ссылки на эти данные. Побитовые копии нарушают это предположение.
  • Глубокая логическая копия. В этом случае мы создаем копию объекта, но фактически не выполняем его по-разному - мы хотим, чтобы объект, который ведет себя одинаково для всех целей и целей, но не является (обязательно) идентичным памяти клоном оригинала - руководство Objective C называет такой объект "функционально независимым" от него оригинальным. Поскольку механизмы создания этих "интеллектуальных" копий варьируются от класса к классу, мы просим сами объекты выполнить их. Это протокол NSCopying.

Вы хотите последнего. Если это один из ваших собственных объектов, вам нужно просто принять протокол NSCopying и реализовать - (id) copyWithZone: (NSZone *) зону. Вы свободны делать все, что хотите; хотя идея заключается в том, что вы делаете реальную копию себя и возвращаете ее. Вы вызываете 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;
}

Ответ 2

В документации 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;
}

Ответ 3

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

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

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

Ответ 4

another.obj = [obj copyWithZone: zone];

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

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

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

или

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

Ответ 5

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

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

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