Я прочитал NSCopying
документацию, но все еще не уверен, как реализовать то, что требуется.
Мой класс Vendor
:
@interface Vendor : NSObject
{
NSString *vendorID;
NSMutableArray *availableCars;
BOOL atAirport;
}
@property (nonatomic, copy) NSString *vendorID;
@property (nonatomic, retain) NSMutableArray *availableCars;
@property (nonatomic, assign) BOOL atAirport;
- (id)initFromVehVendorAvailsDictionary:(NSDictionary *)vehVendorAvails;
@end
У Vendor
класса есть массив вызываемых объектов Car
.
Мой Car
объект:
@interface Car : NSObject
{
BOOL isAvailable;
NSString *transmissionType;
NSMutableArray *vehicleCharges;
NSMutableArray *fees;
}
@property (nonatomic, assign) BOOL isAvailable;
@property (nonatomic, copy) NSString *transmissionType;
@property (nonatomic, retain) NSMutableArray *vehicleCharges;
@property (nonatomic, retain) NSMutableArray *fees;
- (id) initFromVehicleDictionary:(NSDictionary *)vehicleDictionary;
@end
Итак, Vendor
содержит массив Car
объектов. Car
содержит 2 массива других настраиваемых объектов.
Оба Vendor
и Car
инициализируются из словаря. Я добавлю один из этих методов, они могут иметь значение, а могут и не быть.
-(id)initFromVehVendorAvailsDictionary:(NSDictionary *)vehVendorAvails {
self.vendorCode = [[vehVendorAvails objectForKey:@"Vendor"]
objectForKey:@"@Code"];
self.vendorName = [[vehVendorAvails objectForKey:@"Vendor"]
objectForKey:@"@CompanyShortName"];
self.vendorDivision = [[vehVendorAvails objectForKey:@"Vendor"]
objectForKey:@"@Division"];
self.locationCode = [[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"@Code"];
self.atAirport = [[[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"@AtAirport"] boolValue];
self.venLocationName = [[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"@Name"];
self.venAddress = [[[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"Address"]
objectForKey:@"AddressLine"];
self.venCountryCode = [[[[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"Address"]
objectForKey:@"CountryName"]
objectForKey:@"@Code"];
self.venPhone = [[[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"Telephone"]
objectForKey:@"@PhoneNumber"];
availableCars = [[NSMutableArray alloc] init];
NSMutableArray *cars = (NSMutableArray *)[vehVendorAvails objectForKey:@"VehAvails"];
for (int i = 0; i < [cars count]; i++) {
Car *car = [[Car alloc] initFromVehicleDictionary:[cars objectAtIndex:i]];
[availableCars addObject:car];
[car release];
}
self.venLogo = [[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"TPA_Extensions"]
objectForKey:@"VendorPictureURL"];
return self;
}
Итак, чтобы резюмировать страшную проблему.
Мне нужно скопировать массив Vendor
объектов. Я считаю, что мне нужно реализовать NSCopying
протокол Vendor
, что может означать, что мне нужно реализовать его также, Car
поскольку он Vendor
содержит массив Car
s. Это означает, что мне также нужно реализовать его в классах, которые содержатся в двух массивах, принадлежащих Car
объекту.
Я был бы очень признателен, если бы мог получить некоторые рекомендации по реализации NSCopying
протокола Vendor
, я нигде не могу найти никаких руководств по этому поводу.
источник
Ответы:
Чтобы реализовать NSCopying , ваш объект должен реагировать на
-copyWithZone:
селектор. Вот как вы заявляете, что соблюдаете его:@interface MyObject : NSObject <NSCopying> {
Затем в реализации вашего объекта (ваш
.m
файл):- (id)copyWithZone:(NSZone *)zone { // Copying code here. }
Что должен делать ваш код? Сначала создайте новый экземпляр объекта - вы можете вызвать его,
[[[self class] alloc] init]
чтобы получить инициализированный объект текущего класса, который хорошо работает для создания подклассов. Затем для любых переменных экземпляра, которые являются подклассомNSObject
, поддерживающим копирование, вы можете вызвать[thatObject copyWithZone:zone]
новый объект. Для примитивных типов (int
,char
,BOOL
и друзья) просто установить переменные равными. Итак, для вашего объекта Vendor это будет выглядеть так:- (id)copyWithZone:(NSZone *)zone { id copy = [[[self class] alloc] init]; if (copy) { // Copy NSObject subclasses [copy setVendorID:[[self.vendorID copyWithZone:zone] autorelease]]; [copy setAvailableCars:[[self.availableCars copyWithZone:zone] autorelease]]; // Set primitives [copy setAtAirport:self.atAirport]; } return copy; }
источник
copy
обычно реализуется как неглубокая копия, как показал Джефф. Это необычно - хотя и возможно - что вам нужна полная глубокая копия (где все копируется полностью). Глубокие копии тоже доставляют гораздо больше проблем, поэтому обычно нужно быть уверенным, что это действительно то, что вам нужно.copyWithZone:
возвращает объект со счетчиком ссылок 1 и без автоматического выпуска, это приведет к утечке. Нужно добавить как минимум автоспуск.[[self class] alloc]
использоватьallocWithZone
вместо этого? Извините за то, что поднял этот вопрос.-copy
методы также делают глубокие копии .Этот ответ аналогичен принятому, но использует
allocWithZone:
и обновляется для ARC. NSZone - это базовый класс для распределения памяти. Хотя игнорированиеNSZone
может работать в большинстве случаев, это все же неверно.Для правильной реализации
NSCopying
вы должны реализовать метод протокола, который выделяет новую копию объекта со свойствами, соответствующими значениям оригинала.В объявлении интерфейса в заголовке укажите, что ваш класс реализует
NSCopying
протокол:@interface Car : NSObject<NSCopying> { ... }
В реализации .m добавьте
-(id)copyWithZone
метод, который выглядит примерно так:- (id)copyWithZone:(NSZone*)zone { Car* carCopy = [[[self class] allocWithZone:zone] init]; if (carCopy) { carCopy.isAvailable = _isAvailable; carCopy.transmissionType = _transmissionType; ... // assign all other properties. } return carCopy; }
источник
Быстрая версия
Просто позвоните,
object.copy()
чтобы создать копию.Я не использовал
copy()
для типов значений, поскольку они копируются «автоматически». Но пришлось использоватьcopy()
дляclass
типов.Я проигнорировал
NSZone
параметр, потому что в документах говорится, что он устарел:Также обратите внимание, что это упрощенная реализация. Если у вас есть подклассы он получает немного Tricker и вы должны использовать динамический тип:
type(of: self).init(transmissionType: transmissionType)
.class Vendor { let vendorId: String var availableCars: [Car] = [] init(vendorId: String) { self.vendorId = vendorId } } extension Vendor: NSCopying { func copy(with zone: NSZone? = nil) -> Any { let copy = Vendor(vendorId: vendorId) if let availableCarsCopy = availableCars.map({$0.copy()}) as? [Car] { copy.availableCars = availableCarsCopy } return copy } } class Car { let transmissionType: String var isAvailable: Bool = false var fees: [Double] = [] init(transmissionType: String) { self.transmissionType = transmissionType } } extension Car: NSCopying { func copy(with zone: NSZone? = nil) -> Any { let copy = Car(transmissionType: transmissionType) copy.isAvailable = isAvailable copy.fees = fees return copy } }
источник