web-dev-qa-db-fra.com

Implémentation de NSCopying

J'ai lu les documents NSCopying mais je ne sais toujours pas comment implémenter ce qui est requis.

Ma 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 possède un tableau d'objets appelé Car.

Mon objet Car:

@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

Ainsi, Vendor contient un tableau d'objets Car. Car contient 2 tableaux d'autres objets personnalisés.

Vendor et Car proviennent tous les deux d'un dictionnaire. J'ajouterai l'une de ces méthodes, elles peuvent ou non être pertinentes.

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

Donc, pour résumer le problème effrayant.

J'ai besoin de copier un tableau d'objets Vendor. Je crois que je dois implémenter le protocole NSCopying sur Vendor, ce qui peut signifier que je dois l'implémenter également sur Car puisque Vendor contient un tableau de Cars. Cela signifie que je dois également l'implémenter sur les classes contenues dans les 2 tableaux appartenant à l'objet Car.

J'apprécierais vraiment si je pouvais obtenir des conseils sur la mise en œuvre du protocole NSCopying sur Vendor, je ne trouve aucun tutoriel à ce sujet nulle part.

78
user440096

Pour implémenter NSCopying , votre objet doit répondre au -copyWithZone: sélecteur. Voici comment vous déclarez vous y conformer:

@interface MyObject : NSObject <NSCopying> {

Ensuite, dans l'implémentation de votre objet (votre .m fichier):

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

Que doit faire votre code? Créez d'abord une nouvelle instance de l'objet - vous pouvez appeler [[[self class] alloc] init] pour obtenir un objet initialisé de la classe actuelle, qui fonctionne bien pour le sous-classement. Ensuite, pour toutes les variables d'instance qui sont une sous-classe de NSObject qui prend en charge la copie, vous pouvez appeler [thatObject copyWithZone:zone] pour le nouvel objet. Pour les types primitifs (int, char, BOOL et amis), définissez simplement les variables comme égales. Donc, pour votre fournisseur obejct, cela ressemblerait à ceci:

- (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;
}
179
Jeff Kelley

Cette réponse est similaire à celle acceptée, mais utilise allocWithZone: Et est mise à jour pour ARC. NSZone est une classe de base pour l'allocation de mémoire. Même si ignorer NSZone peut fonctionner dans la plupart des cas, il est toujours incorrect.

Pour implémenter correctement NSCopying, vous devez implémenter une méthode de protocole qui alloue une nouvelle copie de l'objet, avec des propriétés qui correspondent aux valeurs de l'original.

Dans la déclaration d'interface dans l'en-tête, spécifiez que votre classe implémente le protocole NSCopying:

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

Dans l'implémentation .m, ajoutez une méthode -(id)copyWithZone qui ressemble à ceci:

- (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;
}
6
Justin Meiners

Version Swift

Appelez simplement object.copy() pour créer la copie.

Je n'ai pas utilisé copy() pour les types de valeur car ceux-ci sont copiés "automatiquement". Mais j'ai dû utiliser copy() pour class types.

J'ai ignoré le paramètre NSZone car docs dit qu'il est obsolète:

Ce paramètre est ignoré. Les zones de mémoire ne sont plus utilisées par Objective-C.

Veuillez également noter qu'il s'agit d'une implémentation simplifiée. Si vous avez des sous-classes cela devient un peu plus délicat et vous devez utiliser le type dynamique: 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
    }
}
1
kgaidis