web-dev-qa-db-fra.com

Propriétés en lecture seule dans Objective-C?

J'ai déclaré une propriété readonly dans mon interface en tant que telle:

 @property (readonly, nonatomic, copy) NSString* eventDomain;

Je ne comprends peut-être pas bien les propriétés, mais je pensais que lorsque vous le déclariez comme readonly, vous pouvez utiliser le séparateur généré à l'intérieur de l'implémentation (.m), mais les entités externes ne peuvent pas changer la valeur. This SO question indique que cela devrait se produire. C’est le comportement que je suis après. Cependant, lorsque vous essayez d’utiliser la syntaxe standard de définition ou de point pour définir eventDomain à l'intérieur de ma méthode init, cela me donne un unrecognized selector sent to instance. Erreur. Bien sûr, je suis @synthesizeing la propriété. Essayer de l'utiliser comme ça:

 // inside one of my init methods
 [self setEventDomain:@"someString"]; // unrecognized selector sent to instance error

Alors, est-ce que je comprends mal la déclaration readonly sur une propriété? Ou est-ce qu'il se passe autre chose?

87
Alex

Vous devez indiquer au compilateur que vous souhaitez également un séparateur. Une méthode courante consiste à le placer dans un extension de classe dans le fichier .m:

@interface YourClass ()

@property (nonatomic, copy) NSString* eventDomain;

@end
109
Eiko

Une autre façon de travailler avec les propriétés en lecture seule consiste à utiliser @synthesize pour spécifier le magasin de support. Par exemple

@interface MyClass

@property (readonly) int whatever;

@end

Puis dans la mise en œuvre

@implementation MyClass

@synthesize whatever = _whatever;

@end

Vos méthodes peuvent alors définir _wwhat, puisqu'il s'agit d'une variable membre.


Une autre chose intéressante que j'ai réalisée ces derniers jours est que vous pouvez créer des propriétés en écriture qui sont inscriptibles par des sous-classes telles que:

(dans le fichier d'en-tête)

@interface MyClass
{
    @protected
    int _propertyBackingStore;
}

@property (readonly) int myProperty;

@end

Ensuite, dans la mise en œuvre

@synthesize myProperty = _propertyBackingStore;

Il utilisera la déclaration dans le fichier d'en-tête afin que les sous-classes puissent mettre à jour la valeur de la propriété, tout en conservant sa lecture seule.

Un peu malheureusement en termes de dissimulation et d’encapsulation de données.

35
yano

Eiko et d'autres ont donné des réponses correctes.

Voici un moyen plus simple: Accédez directement à la variable membre privée.

Exemple

Dans le fichier d'en-tête .h:

@property (strong, nonatomic, readonly) NSString* foo;

Dans le fichier d'implémentation .m:

// inside one of my init methods
self->_foo = @"someString"; // Notice the underscore prefix of var name.

C’est tout, c’est tout ce dont vous avez besoin. Pas de mois, pas de chichi.

Détails

À partir de Xcode 4.4 et de LLVM Compiler 4.0 ( Nouvelles fonctionnalités de Xcode 4.4 ), vous n'avez pas besoin de jouer avec les tâches abordées dans les autres réponses:

  • Le mot clé synthesize
  • Déclarer une variable
  • Nouvelle déclaration de la propriété dans le fichier .m d'implémentation.

Après avoir déclaré une propriété foo, vous pouvez supposer que Xcode a ajouté une variable membre privée nommée avec un préfixe de soulignement: _foo.

Si la propriété a été déclarée readwrite, Xcode génère une méthode de lecture nommée foo et un outil de configuration nommé setFoo. Ces méthodes sont appelées implicitement lorsque vous utilisez la notation pointée (mon Object.myMethod). Si la propriété a été déclarée readonly, aucun séparateur n'est généré. Cela signifie que la variable de support, nommée avec le trait de soulignement, est pas elle-même en lecture seule. readonly signifie simplement qu'aucune méthode de définition n'a été synthétisée et que, par conséquent, l'utilisation de la notation par points pour définir une valeur échoue avec une erreur du compilateur. La notation par points échoue car le compilateur vous empêche d'appeler une méthode (le configurateur) qui n'existe pas.

Le moyen le plus simple consiste à accéder directement à la variable membre, nommée avec le trait de soulignement. Vous pouvez le faire même sans déclarer cette variable nommée underscore! Xcode insère cette déclaration dans le cadre du processus de construction/compilation, de sorte que votre code compilé aura bien la déclaration de variable. Mais vous ne voyez jamais cette déclaration dans votre fichier de code source original. Pas de magie, juste sucre syntaxique .

En utilisant self-> est un moyen d'accéder à une variable membre de l'objet/instance. Vous pourrez peut-être omettre cela et utiliser simplement le nom var. Mais je préfère utiliser la flèche auto + car cela rend mon code auto-documenté. Quand vous voyez le self->_foo vous savez sans ambiguïté que _foo est une variable membre de cette instance.


En passant, discuter des avantages et des inconvénients des accesseurs de propriété par rapport à l'accès direct à ivar est exactement le type de traitement réfléchi que vous lirez dans Dr. Matt Neuberg 's Programming iOS livre. J'ai trouvé très utile de lire et de relire.

34
Basil Bourque

Voir Personnalisation des classes existantes dans les documents iOS.

readonly Indique que la propriété est en lecture seule. Si vous spécifiez en lecture seule, seule une méthode de lecture est requise dans @implementation. Si vous utilisez @synthesize dans le bloc d'implémentation, seule la méthode getter est synthétisée. De plus, si vous essayez d'affecter une valeur à l'aide de la syntaxe à points, vous obtenez une erreur de compilation.

Les propriétés en lecture seule ont uniquement une méthode getter. Vous pouvez toujours définir l'ivar de support directement dans la classe de la propriété ou à l'aide du codage de valeur de clé.

20
Jonah

Vous comprenez mal l'autre question. Dans cette question il y a une extension de classe, déclarée ainsi:

@interface MYShapeEditorDocument ()
@property (readwrite, copy) NSArray *shapesInOrderBackToFront;
@end

C’est ce qui génère le sélecteur visible uniquement dans l’implémentation de la classe. Ainsi, comme le dit Eiko, vous devez déclarer une extension de classe et remplacer la déclaration de propriété pour indiquer au compilateur de générer un programme de définition uniquement dans la classe.

7
BoltClock

La solution la plus courte est:

MyClass.h

@interface MyClass {

  int myProperty;

}

@property (readonly) int myProperty;

@end

MyClass.h

@implementation MyClass

@synthesize myProperty;

@end
5
user2159978

Si une propriété est définie comme étant en lecture seule, cela signifie qu’il n’y aura effectivement pas de séparateur utilisable en interne pour la classe ou en externe à partir d’autres classes. (i.e .: Vous aurez un "getter" seulement si cela a du sens.)

A partir de là, vous voulez une propriété de lecture/écriture normale marquée comme privée, que vous pouvez obtenir en définissant la variable de classe comme privée dans votre fichier d'interface en tant que telle:

@private
    NSString* eventDomain;
}
2
John Parker