web-dev-qa-db-fra.com

NSArray @property soutenu par un NSMutableArray

J'ai défini une classe dans laquelle j'aimerais qu'une propriété publique apparaisse comme si elle était soutenue par une NSArray. C’est assez simple, mais dans mon cas, le support réel ivar est un NSMutableArray:

@interface Foo
{
    NSMutableArray* array;
}
@property (nonatomic, retain) NSArray* array;

@end

Dans mon fichier d'implémentation (*.m), je @synthesize la propriété, mais je suis immédiatement confronté à des avertissements, car utiliser self.words revient à essayer de modifier une NSArray.

Quelle est la bonne façon de faire cela?

Merci!

39
Matty P

Je déclarerais une readonlyNSArray dans votre en-tête et remplacerais le getter pour que ce tableau renvoie une copie d'une NSMutableArray privée déclarée dans votre implémentation. Considérer ce qui suit.

Foo.h

@interface Foo

@property (nonatomic, retain, readonly) NSArray *array;

@end

Foo.m

@interface Foo ()

@property (nonatomic, retain) NSMutableArray *mutableArray

@end

#pragma mark -

@implementation Foo

@synthesize mutableArray;

- (NSArray *)array
{
    return [[self.mutableArray copy] autorelease];
}

@end
21
Mark Adams

Fondamentalement, placez la propriété NSArray dans une catégorie dans votre fichier d’en-tête et la propriété NSMutableArray dans une extension class dans votre fichier d’implémentation. Ainsi...

Foo.h:

@interface Foo
@end

@interface Foo (Collections)
@property (nonatomic, readonly, strong) NSArray *someArray;
@end

Foo.m

@interface Foo ()
@property (nonatomic, readwrite, strong) NSMutableArray *someArray;
@end
14
sam

Simple:

1) N'utilisez pas une propriété quand ce n'en est pas une.

2) Le code se simplifie pour:

- (NSArray *)currentArray {
    return [NSArray arraywithArray:mutableArray]; // need the arrayWithArray -    otherwise the caller could be in for surprise when the supposedly unchanging array changes while he is using it. 
}

- (void)setArray:(NSArray *)array {
    [mutableArray setArray:array];
}

Quand l'objet est alloué créer le tableau, quand il meurt, dealloc le tableau. 

Lorsque des effets importants se produisent à la simple utilisation d'un '.' opérateur, il est facile de négliger le code extrêmement inefficace. Les accessoires ne sont que ça. De plus, si quelqu'un appelle aFoo.array, le contrat consiste à avoir accès aux membres du tableau de foo, mais il ne s'agit en réalité que d'une copie au moment de l'appel. La différence est suffisamment réelle pour causer des bogues dans les autres implémentations publiées ici.

5
Tom Andersen

Mise à jour: cette réponse n'est plus valide. Utilisez l’une des solutions suggérées ci-dessous.

Ces jours-ci, vous pouvez faire ce qui suit:

Foo.m:

@implementation Foo {
    NSMutableArray* _array;
}

@end

Foo.h:

@interface Foo

@property (readonly, strong) NSArray* array;

@end

Vous pouvez toujours adresser _array mutable par ivar depuis l’intérieur de l’implémentation et de l’extérieur, il sera accessible via une propriété immuable. Malheureusement, cela ne garantit pas que les autres utilisateurs ne pourront pas le transtyper vers NSMutableArray et le modifier. Pour une meilleure protection contre les idiots, vous devez définir la méthode d'accès et renvoyer une copie immuable, mais cela peut s'avérer très coûteux dans certains cas.

En fait, je suis d’accord avec l’un des commentaires ci-dessus selon lequel il est préférable d’utiliser des méthodes simples d’accesseur si vous devez renvoyer des données en lecture seule, c’est nettement moins ambigu. 

4
highmaintenance

En effet, votre propriété doit correspondre au type de classe de l'IVAR.

Une solution possible/solution de contournement:

//Foo.h:
@interface Foo
{
    NSMutableArray* mutableArray;
}
@property (readwrite, nonatomic, retain) NSArray* array;
//or manual accessor declarations, in case you're picky about wrapper-properties.
@end

//Foo.m:
@interface Foo ()
@property (readwrite, nonatomic, retain) NSMutableArray* mutableArray;
@end

@implementation

@synthesize mutableArray;
@dynamic array;

- (NSArray *)array {
    return [NSArray arrayWithArray:self.mutableArray];
}

- (void)setArray:(NSArray *)array {
    self.mutableArray = [NSMutableArray arrayWithArray:array];
}

@end

Vous ajoutez une propriété privée mutableArray dans une extension de classe et vous transmettez simplement la variable publique array à votre propriété mutable privée.

Avec les extensions linguistiques les plus récentes d’ObjC, j’ai tendance à supprimer le

{
    NSMutableArray* mutableArray;
}

ivar bloc entièrement, si possible.

Et définir l’ivar à travers la synthèse, en tant que tel:

@synthesize mutableArray = _mutableArray;

qui générera une instance NSMutableArray *_mutableArray; pour vous.

2
Regexident

Réponse la plus simple: votre type de propriété (NSArray) ne correspond pas à votre type de variable d'instance (NSMutableArray).

C’est une autre bonne raison pour laquelle vous ne devez pas définir vos propres variables de sauvegarde. Laissez @synthesize configurer vos variables d'instance; ne le faites pas à la main.

0
cdstamper