He leído el NSCopying
documentos, pero todavía no estoy seguro de cómo implementar lo que se requiere.
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
los Vendor
clase tiene una matriz 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
Entonces, Vendor
contiene una matriz deCar
objetos. Car
contiene 2 matrices de otros objetos personalizados.
Ambos Vendor
y Car
son iniciales 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;
}
Entonces, para resumir el aterrador problema.
Necesito copiar una serie de Vendor
objetos. Creo que necesito implementar el NSCopying
protocolo Vendor
, lo que puede significar que debo implementarlo también, Car
ya que Vendor
contiene una matriz de Car
s. Eso significa que también necesito implementarlo en las clases que se encuentran en las 2 matrices que pertenecen al Car
objeto.
Realmente agradecería si pudiera obtener alguna orientación sobre la implementación del NSCopying
protocolo Vendor
, no puedo encontrar tutoriales sobre esto en ningún lado.
fuente
Respuestas:
Para implementar NSCopying , su objeto debe responder al
-copyWithZone:
selector. Así es como declara que lo cumple:@interface MyObject : NSObject <NSCopying> {
Luego, en la implementación de su objeto (su
.m
archivo):- (id)copyWithZone:(NSZone *)zone { // Copying code here. }
¿Qué debería hacer tu código? Primero, cree una nueva instancia del objeto; puede llamar
[[[self class] alloc] init]
para obtener un objeto inicializado de la clase actual, que funciona bien para subclases. Luego, para cualquier variable de instancia que sea una subclase deNSObject
que admita la copia, puede solicitar[thatObject copyWithZone:zone]
el nuevo objeto. Para los tipos primitivos (int
,char
,BOOL
y amigos) acaba de establecer las variables a ser igual. Por lo tanto, para su proveedor concreto, 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; }
fuente
copy
normalmente se implementa como una copia superficial como mostró Jeff. Es inusual, aunque no inconcebible, que desee una copia en profundidad completa (donde se copia todo el camino hacia abajo). Las copias profundas también son mucho más problemáticas, por lo que generalmente desea estar seguro de que eso es realmente lo que desea.copyWithZone:
devuelve un objeto con un recuento de referencia de 1 y sin liberación automática, esto causará una fuga. Debe agregar al menos una liberación automática.[[self class] alloc]
usarallocWithZone
en su lugar? Perdón por mencionar esto.-copy
métodos también hagan copias profundas. .Esta respuesta es similar a la aceptada, pero usa
allocWithZone:
y está actualizada para ARC. NSZone es una clase básica para asignar memoria. Mientras ignoraNSZone
puede funcionar en la mayoría de los casos, sigue siendo incorrecto.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 el encabezado, especifique que su clase implementa el
NSCopying
protocolo:@interface Car : NSObject<NSCopying> { ... }
En la implementación .m agregue un
-(id)copyWithZone
método que se parece a lo 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; }
fuente
Versión rápida
Simplemente llame
object.copy()
para crear la copia.No utilicé
copy()
para tipos de valor ya que se copian "automáticamente". Pero tuve que usarcopy()
paraclass
tipos.Ignoré el
NSZone
parámetro porque los documentos dicen que está obsoleto:Además, tenga en cuenta que se trata de una implementación simplificada. Si tiene subclases se pone un poco Tricker y se debe utilizar 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 } }
fuente