Implementazione di NSCopying


84

Ho letto i NSCopyingdocumenti ma sono ancora molto incerto su come implementare ciò che è richiesto.

La mia classe 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

La Vendorclasse ha un array di oggetti chiamati Car.

Il mio Caroggetto:

@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

Quindi, Vendorcontiene una serie di Caroggetti. Carcontiene 2 array di altri oggetti personalizzati.

Entrambi Vendore Carsono init da un dizionario. Aggiungerò uno di questi metodi, possono o non possono essere rilevanti.

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

Quindi, per riassumere il problema spaventoso.

Ho bisogno di copiare un array di Vendoroggetti. Credo di aver bisogno di implementare il NSCopyingprotocollo su Vendor, il che potrebbe significare che devo implementarlo anche su Carpoiché Vendorcontiene un array di Cars. Ciò significa che devo implementarlo anche sulle classi che si trovano nei 2 array appartenenti Carall'oggetto.

Apprezzerei davvero se potessi ottenere alcune indicazioni sull'implementazione del NSCopyingprotocollo Vendor, non riesco a trovare nessun tutorial su questo da nessuna parte.


Hai letto la documentazione di NSCopying? L'ho trovato abbastanza chiaro quando necessario.
jv42

4
Sì, leggi e rileggi. Raramente trovo i documenti Apple facili da imparare, sebbene siano ottimi per trovare metodi ecc. Durante la programmazione. Grazie -Code

Risposte:


186

Per implementare NSCopying , il tuo oggetto deve rispondere al -copyWithZone:selettore. Ecco come dichiari di conformarti ad esso:

@interface MyObject : NSObject <NSCopying> {

Quindi, nell'implementazione del tuo oggetto (il tuo .mfile):

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

Cosa dovrebbe fare il tuo codice? Innanzitutto, crea una nuova istanza dell'oggetto: puoi chiamare [[[self class] alloc] init]per ottenere un oggetto inizializzato della classe corrente, che funziona bene per le sottoclassi. Quindi, per qualsiasi variabile di istanza che è una sottoclasse di NSObjectquella che supporta la copia, puoi chiamare [thatObject copyWithZone:zone]il nuovo oggetto. Per i tipi primitivi ( int, char, BOOLe gli amici) è sufficiente impostare le variabili di essere uguali. Quindi, per il tuo venditore di oggetti, sarebbe simile a questo:

- (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
@Code: copyè tipicamente implementato come una copia superficiale come ha mostrato Jeff. È insolito - anche se non inconcepibile - che tu voglia una copia completa completa (dove viene copiato tutto fino in fondo). Anche le copie profonde sono molto più problematiche, quindi in genere vuoi essere sicuro che sia davvero quello che vuoi.
Chuck

3
C'è un problema nel codice in cui si copiano le sottoclassi, poiché copyWithZone:restituisce un oggetto con conteggio dei riferimenti pari a 1 e nessun rilascio automatico ciò causerà una perdita. Devi aggiungere almeno un rilascio automatico.
Marius

22
Non dovresti [[self class] alloc]usare allocWithZoneinvece? Scusa per aver sollevato questo problema.
jweyrich

1
Gente, suppongo che usando ARC (poiché il minimo IOS supportato per qualsiasi app è 4.3), non devi preoccuparti del rilascio e del rilascio automatico.
rishabh

1
@GeneralMike: questa dovrebbe probabilmente essere una domanda separata, ma in generale (vedi cosa ho fatto lì?), Vuoi assicurarti di copiare ogni oggetto dall'originale durante una copia profonda e assicurarti che i loro -copymetodi eseguano anche copie approfondite .
Jeff Kelley,

6

Questa risposta è simile a quella accettata, ma utilizza allocWithZone:e viene aggiornata per ARC. NSZone è la classe base per l'allocazione della memoria. Anche se ignorare NSZonepotrebbe funzionare per la maggior parte dei casi, non è comunque corretto.

Per implementare correttamente NSCopyingè necessario implementare un metodo di protocollo che alloca una nuova copia dell'oggetto, con proprietà che corrispondono ai valori dell'originale.

Nella dichiarazione dell'interfaccia nell'intestazione, specifica che la tua classe implementa il NSCopyingprotocollo:

@interface Car : NSObject<NSCopying>
{
 ...
}

Nell'implementazione .m aggiungi un -(id)copyWithZonemetodo simile al seguente:

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

2

Versione rapida

Basta chiamare object.copy()per creare la copia.

Non ho usato copy()per i tipi di valore poiché questi vengono copiati "automaticamente". Ma ho dovuto usare copy()per i classtipi.

Ho ignorato il NSZoneparametro perché i documenti dicono che è deprecato:

Questo parametro viene ignorato. Le zone di memoria non sono più utilizzate da Objective-C.

Inoltre, tieni presente che questa è un'implementazione semplificata. Se si dispone di sottoclassi diventa un po 'Tricker e si dovrebbe utilizzare tipo dinamico: 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
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.