2010-11-03 11 views
76

He leído los documentos NSCopying pero aún no estoy seguro de cómo implementar lo que se requiere.Implementación de NSCopying

Mi clase 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 clase Vendor tiene una serie de objetos llamados Car.

Mi Car objeto:

@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 

Así, Vendor contiene una matriz de objetos Car. Car contiene 2 matrices de otros objetos personalizados.

Ambos Vendor y Car son init de un diccionario. Agregaré uno de estos métodos, pueden ser relevantes o no.

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

Así que para resumir el problema de miedo.

Necesito copiar una matriz de objetos Vendor. Creo que necesito implementar el protocolo NSCopying en Vendor, lo que puede significar que necesito implementarlo también en Car ya que Vendor contiene una matriz de Car s. Eso significa que también necesito implementarlo en las clases que se mantienen en las 2 matrices que pertenecen al objeto Car.

Realmente apreciaría si pudiera obtener alguna orientación sobre la implementación del protocolo NSCopying en Vendor, no puedo encontrar ningún tutorial sobre este tema en ninguna parte.

+0

¿Has leído la documentación de NSCopying? Lo encontré bastante claro cuando lo necesitaba. – jv42

+4

Sí, léalo y vuelva a leerlo. Raramente encuentro que los documentos Apple son fáciles de aprender, aunque son geniales para encontrar métodos, etc. durante la programación. Gracias -Código –

Respuesta

173

Para implementar NSCopying, su objeto debe responder al selector -copyWithZone:. Así es como usted declara que conformarse a ella:

@interface MyObject : NSObject <NSCopying> { 

A continuación, en la ejecución de su objeto (el archivo de .m):

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

¿Qué debe hacer su código? Primero, cree una nueva instancia del objeto: puede llamar al [[[self class] alloc] init] para obtener un mensaje inicializado de la clase actual, que funciona bien para la creación de subclases. Luego, para cualquier variable de instancia que sea una subclase de NSObject que admita la copia, puede llamar al [thatObject copyWithZone:zone] para el nuevo objeto. Para los tipos primitivos (int, char, BOOL y amigos) simplemente configure las variables para que sean iguales. Por lo tanto, para su proveedor obejct, se vería así:

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

Hola Jeff, ¿Es realmente tan simple como eso? Además, ¿debo aplicar las mismas cosas a la clase Car y a todos los objetos personalizados de los que contiene arrays? Básicamente, baja todo el camino. ¿O solo debería aplicarse al proveedor? Muchas gracias, –

+1

@Code: 'copy' se implementa típicamente como una copia superficial como Jeff mostró. Es inusual, aunque no inconcebible, que quieras una copia profunda completa (donde todo está completamente copiado). Las copias profundas son mucho más problemas también, por lo que generalmente quiere asegurarse de que es realmente lo que quiere. – Chuck

+0

@Code: querrá implementar esto para todo lo que planee copiar con '-copy' o' -copyWithZone: '. Tenga en cuenta que, como dice @Chuck, esta será una "copia superficial", por lo que las matrices pueden señalar los mismos objetos en el original y la copia. Si esos objetos cambian, es posible que deba copiarlos también. –

4

Esta respuesta es similar a la aceptada, pero utiliza allocWithZone: y se actualiza de ARC. NSZone es una clase básica para asignar memoria. Si bien ignorar NSZone podría funcionar en la mayoría de los casos, sigue siendo incorrecto.

Para implementar correctamente NSCopying debe implementar un método de protocolo que asigne una nueva copia del objeto, con propiedades que coincidan con los valores del original.

En la declaración de interfaz en la cabecera, se especifica que la clase implementa el protocolo NSCopying:

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

En la implementación .m añadir un método -(id)copyWithZone que será similar a la siguiente:

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

Swift Versión

Simplemente llame al object.copy() para crear la copia.

No utilicé copy() para tipos de valores, ya que se copian "automáticamente". Pero tuve que usar copy() para los tipos class.

IGNORÉ el parámetro NSZone porque docs dicen que está en desuso:

Este parámetro se ignora. Las zonas de memoria ya no son utilizadas por Objective-C.

Además, tenga en cuenta que se trata de una implementación simplificada. Si tiene subclases, se pone un poco complicado y debe usar el tipo dinámico: 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 
    } 
}