86 votes

Mise en œuvre de NSCopying

J'ai lu le NSCopying mais je ne sais toujours pas comment mettre en œuvre 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

En Vendor possède un tableau d'objets appelés Car .

Mon Car objet :

@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

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

Les deux sites Vendor y Car sont init d'un dictionnaire. Je vais ajouter une de ces méthodes, elles peuvent être pertinentes ou non.

-(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 de Vendor objets. Je pense que je dois implémenter le NSCopying le protocole sur Vendor ce qui peut signifier que je dois l'implémenter aussi sur Car depuis Vendor contient un tableau de Car s. Cela signifie que je dois également l'implémenter sur les classes qui sont contenues dans les 2 tableaux appartenant à l'élément Car objet.

J'apprécierais vraiment si je pouvais avoir des conseils pour mettre en place NSCopying le protocole sur Vendor Je n'ai trouvé aucun tutoriel sur ce sujet.

0 votes

Avez-vous lu la documentation de NSCopying ? Je l'ai trouvée assez claire quand c'était nécessaire.

5 votes

Oui, lisez et relisez-le. Je trouve rarement que les docs d'Apple sont faciles à utiliser pour apprendre, bien qu'ils soient très utiles pour trouver des méthodes, etc. pendant la programmation. Merci - Code

187voto

Jeff Kelley Points 12893

Pour mettre en œuvre NSCopying votre objet doit répondre à l'instruction -copyWithZone: sélecteur. Voici comment déclarer que vous vous y conformez :

@interface MyObject : NSObject <NSCopying> {

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

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

Que doit faire votre code ? Tout d'abord, créez une nouvelle instance de l'objet - vous pouvez appeler [[[self class] alloc] init] pour obtenir un obejct initialisé de la classe courante, ce qui fonctionne bien pour le sous-classement. Ensuite, pour toute variable d'instance qui est une sous-classe de NSObject qui supporte la copie, vous pouvez appeler [thatObject copyWithZone:zone] pour le nouvel objet. Pour les types primitifs ( int , char , BOOL et amis) il suffit de mettre les variables à égalité. Donc, pour votre objet Vendeur, ça ressemblerait à ça :

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

Bonjour Jeff, Est-ce vraiment aussi simple que cela ? Dois-je également appliquer la même chose à la classe Car et à tous les objets personnalisés dont elle contient des tableaux ? En fait, il faut descendre tout le long. Ou faut-il seulement l'appliquer à Vendor ? Merci beaucoup,

2 votes

@Code : copy est typiquement implémenté comme une copie superficielle comme Jeff l'a montré. Il est inhabituel - mais pas inconcevable - que vous souhaitiez une copie complète et profonde (où tout ce qui est en bas est copié). Les copies profondes posent beaucoup plus de problèmes, aussi, vous voulez généralement être sûr que c'est vraiment ce que vous voulez.

0 votes

@Code : Vous devrez implémenter ceci pour tout ce que vous prévoyez de copier. -copy o -copyWithZone: . Notez que, comme le dit @Chuck, il s'agira d'une "copie peu profonde", de sorte que les tableaux peuvent pointer vers les mêmes objets dans l'original et la copie. Si ces objets changent, vous devrez peut-être les copier également.

7voto

Justin Meiners Points 5282

Cette réponse est similaire à celle acceptée, mais utilise allocWithZone: et est mis à jour pour l'ARC. NSZone est une classe de base pour l'allocation de la mémoire. Tout en ignorant NSZone peut fonctionner dans la plupart des cas, elle reste incorrecte.

Pour mettre en œuvre 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 de l'interface dans l'en-tête, spécifiez que votre classe implémente l'interface NSCopying protocole :

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

Dans l'implémentation .m, ajoutez un -(id)copyWithZone qui ressemble à ce qui suit :

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

3voto

anon Points 108

Version Swift

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

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

J'ai ignoré le NSZone car docs dire qu'il est déprécié :

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

Veuillez également noter qu'il s'agit d'une mise en œuvre simplifiée. Si vous avez des sous-classes c'est un peu plus compliqué 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
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X