Внедрение NSCopying - PullRequest
       25

Внедрение NSCopying

78 голосов
/ 03 ноября 2010

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

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

Ответы [ 3 ]

178 голосов
/ 03 ноября 2010

Чтобы реализовать 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;
}
6 голосов
/ 03 января 2017

Этот ответ похож на принятый, но использует 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;
}
1 голос
/ 08 января 2018

Swift Version

Просто позвоните object.copy(), чтобы создать копию.

Я не использовал copy() для типов значений, поскольку они копируются "автоматически«.Но мне пришлось использовать copy() для class типов.

Я проигнорировал параметр NSZone, поскольку документы говорят, что он устарел:

Этот параметр игнорируется.Зоны памяти больше не используются Objective-C.

Кроме того, обратите внимание, что это упрощенная реализация. Если у вас есть подклассы , это немного обманывает, и вы должны использовать динамический тип: 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
    }
}
...