Как правильно реализовать mutableCopyWithZone и copyWithZone
Я прочитал несколько других тем об этом, но все же я потерян.
Я хочу создать 2 вида объектов, один неизменяемый только со свойствами "только для чтения", а другой изменяемый со свойствами "только чтение".
Давайте назовем их EXCar и EXMutableCar.
EXCar является подклассом NSObject, а EXMutableCar является подклассом EXCar.
ExCar будет иметь в своем интерфейсе
@property (nonatomic, strong, readonly) NSString *name;
EXMutableCar будет иметь в своем интерфейсе
@property (nonatomic, strong) NSString *name;
Поэтому я "открываю" свойства EXCar, когда использую его подкласс EXMutableCar. И тогда это изменчиво. Проблема заключается в том, чтобы правильно копировать между ними.
Я реализовал mutableCopyWithZone в EXCar:
- (id)mutableCopyWithZone:(NSZone *)zone {
EXMutableCar *mutableCopy = [[EXMutableCar allocWithZone:zone] init];
mutableCopy.name = _name;
return mutableCopy;
}
Первый вопрос, это хороший способ сделать это? (Я хочу ласточку)
Проблема с copyWithZone. Поскольку свойства EXCar доступны только для чтения, я не могу создать ни в EXCar, ни в EXMutableCar новый экземпляр EXCar и заполнить его свойства следующим образом:
- (id)copyWithZone:(NSZone *)zone {
EXCar *copy = [[EXCar allocWithZone:zone] init];
copy.name = _name; // This can't work...
return copy;
}
И я действительно не хочу делать метод init с 15 свойствами для передачи (наверняка EXCar является примером, реальные классы полны многих свойств). И обычно они инициируются из сообщения JSON с сервера, поэтому им не нужен сложный метод init.
Второй вопрос: как сделать copyWithZone, чтобы мой класс оставался неизменным?
Спасибо за вашу помощь:)
1 ответ
Код:
// EXCar.h
#import <Foundation/Foundation.h>
@interface EXCar : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic, strong, readonly) NSString* name;
@end
// EXCar.m
#import "EXCar.h"
#import "EXMutableCar.h"
@implementation EXCar
- (id)copyWithZone:(NSZone *)zone {
EXCar* car = [[[self class] allocWithZone:zone] init];
car->_name = [_name copyWithZone:zone];
return car;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
EXMutableCar* mutableCar = [[EXMutableCar allocWithZone:zone] init];
mutableCar.name = [_name mutableCopyWithZone:zone];
return mutableCar;
}
@end
// EXMutableCar.h
#import "EXCar.h"
@interface EXMutableCar : EXCar
@property (nonatomic, strong) NSString* name;
@end
// EXMutableCar.m
#import "EXMutableCar.h"
@implementation EXMutableCar
@synthesize name = _mutableName;
- (id)copyWithZone:(NSZone *)zone {
EXMutableCar* car = [super copyWithZone:zone];
car->_mutableName = [_mutableName copyWithZone:zone];
return car;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
EXMutableCar* car = [super mutableCopyWithZone:zone];
car->_mutableName = [_mutableName mutableCopyWithZone:zone];
return car;
}
Объяснение:
EXCar
интерфейс реализует оба "копирующих" протокола;- Подкласс
EXMutableCar
переопределяет то же свойство, делая егоreadwrite
,
Первое, что в EXMutableCar
реализация: вручную @synthesize name
потому что XCode дает нам предупреждение, так как у нас есть то же свойство (но с другим спецификатором доступа) в нашем суперклассе.
Обратите внимание, что мы могли бы дать то же имя нашей переменной экземпляра, как _name
, но важно понимать, что мы объявляем в подклассе другую переменную, так как _name
из суперкласса для нас недоступен.
Далее документация Apple гласит:
Если подкласс наследует NSCopying от своего суперкласса и объявляет дополнительные переменные экземпляра, подкласс должен переопределить
copyWithZone:
правильно обрабатывать свои собственные переменные экземпляра, сначала вызывая реализацию суперкласса.
то же самое для NSMutableCopying:
Если подкласс наследует NSMutableCopying от своего суперкласса и объявляет дополнительные переменные экземпляра, подкласс должен переопределить
mutableCopyWithZone:
правильно обрабатывать свои собственные переменные экземпляра, сначала вызывая реализацию суперкласса.
Мы объявляем дополнительные переменные экземпляра, поэтому мы переопределяем эти методы и в нашем подклассе.
Результат:
EXCar* car = [[EXCar alloc]init]; // car.name is (null)
EXCar* carCopy = [car copy]; // we can do this
EXMutableCar* mutableCar = [car mutableCopy]; // and this
mutableCar.name = @"BMW";
car = [mutableCar copy]; // car.name is now @"BMW"
EXMutableCar* anotherMutableCar = [car mutableCopy]; //anotherMutableCar.name is @"BMW"