Лучшая практика при реализации 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:
-
Лучше ли писать имя класса непосредственно [[Crime allocWithZone:zone] init]
или использовать [[[self Class] allocWithZone:zone] init]
?
-
Можно ли использовать [self month]
для копирования iVars или я должен напрямую обращаться к iVars, т.е. _month
?
Ответы
Ответ 1
-
Вы всегда должны использовать [[self class] allocWithZone:zone]
, чтобы убедиться, что вы создаете копию с использованием соответствующего класса. Пример, который вы даете для 002, показывает, почему: Подклассы вызовут [super copyWithZone:zone]
и ожидают вернуть экземпляр соответствующего класса, а не экземпляр суперкласса.
-
Я напрямую обращаюсь к ivars, поэтому мне не нужно беспокоиться о каких-либо побочных эффектах, которые я могу добавить к устройству свойств (например, генерации уведомлений) позже. Имейте в виду, что подклассы могут переопределять любой метод. В вашем примере вы отправляете два дополнительных сообщения на ivar. Я бы выполнил его следующим образом:
код:
- (id)copyWithZone:(NSZone *)zone {
Crime *newCrime = [super copyWithZone:zone];
newCrime->_month = [_month copyWithZone:zone];
newCrime->_category = [_category copyWithZone:zone];
// etc...
return newCrime;
}
Конечно, копируете ли ivars, сохраняете их или просто назначаете, должны отражать то, что делают сеттеры.
Ответ 2
По умолчанию поведение копии метода copyWithZone:
с предоставленными SDK объектами является "мелкой копией". Это означает, что если вы вызываете объект copyWithZone:
on NSString
, он создаст мелкую копию, но не глубокую копию. Разница между мелкой и глубокой копией:
Неглубокая копия объекта копирует ссылки только на объекты исходного массива и помещает их в новый массив.
Глубокая копия фактически скопирует отдельные объекты, содержащиеся в объекте. Это выполняется путем отправки каждого отдельного объекта в сообщение copyWithZone:
в вашем пользовательском методе класса.
INSHORT: Чтобы получить мелкую копию, вы вызываете retain
или strong
для всех переменных экземпляра. Чтобы получить глубокую копию, вы вызываете copyWithZone:
для всех переменных экземпляра в реализации пользовательского класса copyWithZone:
. Теперь это ваш выбор.
Ответ 3
Это моя модель.
#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
Ответ 4
Как насчет этого, который реализует глубокую копию:
/// 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;
}