2012-03-28 2 views
73

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

// 001: Crime is a subclass of NSObject. 
- (id)copyWithZone:(NSZone *)zone { 
    Crime *newCrime = [[[self class] allocWithZone:zone] init]; 
    if(newCrime) { 
     [newCrime setMonth:[self month]]; 
     [newCrime setCategory:[self category]]; 
     [newCrime setCoordinate:[self coordinate]]; 
     [newCrime setLocationName:[self locationName]]; 
     [newCrime setTitle:[self title]]; 
     [newCrime setSubtitle:[self subtitle]]; 
    } 
    return newCrime; 
} 

// 002: Crime is not a subclass of NSObject. 
- (id)copyWithZone:(NSZone *)zone { 
    Crime *newCrime = [super copyWithZone:zone]; 
    [newCrime setMonth:[self month]]; 
    [newCrime setCategory:[self category]]; 
    [newCrime setCoordinate:[self coordinate]]; 
    [newCrime setLocationName:[self locationName]]; 
    [newCrime setTitle:[self title]]; 
    [newCrime setSubtitle:[self subtitle]]; 
    return newCrime; 
} 

В 001:

  1. Лучше ли написать название класса непосредственно [[Crime allocWithZone:zone] init] или использовать [[[self Class] allocWithZone:zone] init]?

  2. Можно ли использовать [self month] для копирования iVars или я должен получить доступ к iVars напрямую, то есть _month?

ответ

94
  1. Вы должны всегда использовать [[self class] allocWithZone:zone], чтобы убедиться, что вы создаете копию, используя соответствующий класс. Пример, который вы даете для 002, показывает, почему: Подклассы вызовут [super copyWithZone:zone] и ожидают вернуть экземпляр соответствующего класса, а не экземпляр суперкласса.

  2. Я напрямую обращаюсь к ivars, поэтому мне не нужно беспокоиться о каких-либо побочных эффектах, которые я могу добавить к устройству свойств (например, генерации уведомлений) позже. Имейте в виду, что подклассы могут переопределять любой метод. В вашем примере вы отправляете два дополнительных сообщения на ivar. Я бы реализовать его следующим образом:

Код:

- (id)copyWithZone:(NSZone *)zone { 
    Crime *newCrime = [super copyWithZone:zone]; 
    newCrime->_month = [_month copyWithZone:zone]; 
    newCrime->_category = [_category copyWithZone:zone]; 
    // etc... 
    return newCrime; 
} 

Конечно, копировать ли вы Ивар, сохранить их, или просто назначить их должен отражать то, что делают сеттеров.

+32

Какой из этих двух подходов на выбор зависит от того, реализует суперкласс 'NSCopying'. Например, 'NSObject' не делает этого, поэтому вызов' [super copyWithZone: zone] 'будет генерировать исключение. – Costique

+0

Он говорит/Пользователи/ws403216/Рабочий стол/Демо/Демо/Преступление.m: 21: 28: Нет видимого @interface для «NSObject» объявляет селектор «copyWithZone»: «Суперкласс Crime.m в моем случае - NSObject. –

+10

@NitinMalguri Как следует из предыдущего комментария, вы должны вызывать '[super copyWithZone: zone]', если родительский класс поддерживает NSCopying, иначе вы должны вызвать '[[[self class] allocWithZone: zone] init]' и поля для копирования как требуется. – Tony

6

По умолчанию копия copyWithZone: с предоставленными SDK объектами является «мелкой копией». Это означает, что если вы вызываете copyWithZone: на объект NSString, он создаст мелкую копию, но не глубокую копию. Разница между мелкой и глубокой копией:

Неглубокая копия объекта копирует ссылки только на объекты исходного массива и помещает их в новый массив.

Глубокая копия фактически скопирует отдельные объекты, содержащиеся в объекте. Это делается путем отправки каждому отдельному объекту сообщения copyWithZone: в вашем пользовательском методе класса.

INSHORT: Чтобы получить мелкую копию, вы вызываете retain или strong на все переменные экземпляра. Чтобы получить глубокую копию, вы вызываете copyWithZone: на все переменные экземпляра в вашем пользовательском классе copyWithZone:. Теперь это ваш выбор.

0

Это моя модель.

#import <Foundation/Foundation.h> 
@interface RSRFDAModel : NSObject 


@property (nonatomic, assign) NSInteger objectId; 

@property (nonatomic, copy) NSString *name; 

@property (nonatomic, strong) NSArray<RSRFDAModel *> *beans; 


@end 


#import "RSRFDAModel.h" 

@interface RSRFDAModel() <NSCopying> 

@end 

@implementation RSRFDAModel 


-(id)copyWithZone:(NSZone *)zone { 
    RSRFDAModel *model = [[[self class] allocWithZone:zone] init]; 

    model.objectId = self.objectId; 
    model.name = self.name; 
    model.beans = [self.beans mutableCopy]; 

    return model; 
} 

@end 
0

Как насчет этого, реализующих глубокую копию:

/// Class Foo has two properties: month and category 
- (id)copyWithZone:(NSZone *zone) { 
    Foo *newFoo; 
    if ([self.superclass instancesRespondToSelector:@selector(copyWithZone:)]) { 
     newFoo = [super copyWithZone:zone]; 
    } else { 
     newFoo = [[self.class allocWithZone:zone] init]; 
    } 
    newFoo->_month = [_month copyWithZone:zone]; 
    newFoo->_category = [_category copyWithZone:zone]; 
    return newFoo; 
}