2010-11-03 11 views
76

Ho letto i documenti NSCopying ma non sono ancora sicuro di come implementare ciò che è necessario.Implementazione di NSCopying

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 classe Vendor ha una serie di oggetti chiamati Car.

mio Car oggetto:

@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, Vendor detiene un array di oggetti Car. Car contiene 2 matrici di altri oggetti personalizzati.

Entrambi Vendor e Car sono init da un dizionario. Aggiungerò uno di questi metodi, potrebbero o potrebbero non essere pertinenti.

-(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 oggetti Vendor. Credo di dover implementare il protocollo NSCopying su Vendor, il che potrebbe significare che devo implementarlo anche su Car poiché Vendor contiene un array di Car s. Ciò significa che devo anche implementarlo sulle classi che sono contenute nei 2 array appartenenti all'oggetto Car.

Apprezzerei molto se potessi ottenere alcune indicazioni sull'implementazione del protocollo NSCopying su Vendor, non riesco a trovare alcun tutorial su questo ovunque.

+0

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

+4

Sì, leggi e rilegge. Raramente ho trovato doc di Apple facile da imparare, anche se sono ottimi per trovare metodi ecc durante la programmazione. Grazie -Code –

risposta

173

Per implementare NSCopying, l'oggetto deve rispondere al selettore -copyWithZone:. Ecco come si dichiara che si conformi ad esso:

@interface MyObject : NSObject <NSCopying> { 

Poi, in attuazione del vostro oggetto (il file .m):

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

Che 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 la sottoclasse. Quindi, per tutte le variabili di istanza che sono una sottoclasse di NSObject che supporta la copia, è possibile chiamare [thatObject copyWithZone:zone] per il nuovo oggetto. Per i tipi primitivi (int, char, BOOL e amici) è sufficiente impostare le variabili su uguale. Così, per il fornitore obejct, che sarebbe simile a questa:

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

Ciao Jeff, è davvero così semplice? Devo anche applicare le stesse cose alla classe Car e tutti gli oggetti personalizzati che contiene matrici? Fondamentalmente dimettiti per tutto il percorso. O dovrebbe solo essere applicato al venditore? Molte grazie, –

+1

@Code: 'copy' viene in genere implementato come una copia superficiale come ha mostrato Jeff. È insolito, anche se non inconcepibile, che tu voglia una copia completa (su cui viene copiato tutto fino in fondo). Anche le copie profonde sono molto più problematiche, quindi generalmente vuoi essere sicuro che sia proprio quello che vuoi. – Chuck

+0

@Code: ti consigliamo di implementarlo per qualsiasi cosa tu abbia intenzione di copiare con '-copy' o' -copyWithZone: '. Nota che come dice @Chuck, questa sarà una "copia superficiale", quindi gli array potrebbero puntare agli stessi oggetti nell'originale e nella copia. Se tali oggetti cambiano, potrebbe essere necessario copiarli. –

4

Questa risposta è simile a quello accettato, ma utilizza allocWithZone: e viene aggiornato per ARC. NSZone è una classe base per l'allocazione della memoria. Mentre ignorare NSZone potrebbe funzionare per la maggior parte dei casi, non è ancora corretto.

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

Nella dichiarazione di interfaccia nell'intestazione, specificare che la classe implementa il protocollo NSCopying:

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

Nell'implementazione .m aggiungiamo un metodo -(id)copyWithZone che sembra qualcosa di 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; 
} 
0

Versione Swift

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

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

ho ignorato il parametro NSZone perché docs dicono che è deprecato:

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

Inoltre, si noti che questa è un'implementazione semplificata. Se si dispone di sottoclassi, diventa un po 'più complesso e si deve utilizzare il 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 
    } 
}