Внедрение NSCopying

Я прочитал документы 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 являются init из словаря. Я добавлю один из этих методов, они могут быть или не быть релевантными.

-(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. Это означает, что мне также нужно реализовать его на классах, которые хранятся в 2 массивах, принадлежащих объекту Car.

Я бы очень признателен, если бы мог получить некоторые рекомендации по реализации протокола NSCopying на Vendor, я не могу найти никаких учебных пособий в этом месте.

Ответ 1

Чтобы реализовать NSCopying, ваш объект должен ответить на селектор -copyWithZone:. Вот как вы заявляете, что согласны с ним:

@interface MyObject : NSObject <NSCopying> {

Затем в реализации ваших объектов (ваш .m файл):

- (id)copyWithZone:(NSZone *)zone
{
    // Copying code here.
}

Что должен делать ваш код? Во-первых, создайте новый экземпляр объекта - вы можете вызвать [[[self class] alloc] init], чтобы получить инициализированный obejct текущего класса, который хорошо подходит для подкласса. Затем для любых переменных экземпляра, которые являются подклассом NSObject, который поддерживает копирование, вы можете вызвать [thatObject copyWithZone:zone] для нового объекта. Для примитивных типов (int, char, BOOL и друзей) просто установите переменные равными. Итак, для вашего obejct Vendor, itd выглядит так:

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

Ответ 2

Этот ответ аналогичен принятому, но использует 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;
}