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 Carobjeto:
@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, Vendorcontiene una matriz deCar objetos. Carcontiene 2 matrices de otros objetos personalizados.
Ambos Vendory Carson 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 Vendorobjetos. Creo que necesito implementar el NSCopyingprotocolo Vendor, lo que puede significar que debo implementarlo también, Carya que Vendorcontiene una matriz de Cars. Eso significa que también necesito implementarlo en las clases que se encuentran en las 2 matrices que pertenecen al Carobjeto.
Realmente agradecería si pudiera obtener alguna orientación sobre la implementación del NSCopyingprotocolo 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
.marchivo):- (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 deNSObjectque admita la copia, puede solicitar[thatObject copyWithZone:zone]el nuevo objeto. Para los tipos primitivos (int,char,BOOLy 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
copynormalmente 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]usarallocWithZoneen su lugar? Perdón por mencionar esto.-copymé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 ignoraNSZonepuede 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
NSCopyingprotocolo:@interface Car : NSObject<NSCopying> { ... }En la implementación .m agregue un
-(id)copyWithZonemé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()paraclasstipos.Ignoré el
NSZonepará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