Comment déclarer des propriétés de niveau classe dans Objective-C?

205

C'est peut-être évident, mais je ne sais pas comment déclarer les propriétés d'une classe en Objective-C.

J'ai besoin de mettre en cache par classe un dictionnaire et je me demande comment le mettre dans la classe.

mamcx
la source

Réponses:

190

les propriétés ont une signification spécifique dans Objective-C, mais je pense que vous voulez dire quelque chose qui est équivalent à une variable statique? Par exemple, une seule instance pour tous les types de Foo?

Pour déclarer des fonctions de classe dans Objective-C, vous utilisez le préfixe + au lieu de - donc votre implémentation ressemblerait à quelque chose comme:

// Foo.h
@interface Foo {
}

+ (NSDictionary *)dictionary;

// Foo.m
+ (NSDictionary *)dictionary {
  static NSDictionary *fooDict = nil;
  if (fooDict == nil) {
    // create dict
  }
  return fooDict;
}
Andrew Grant
la source
23
Est-ce correct? La définition de fooDict à zéro ne sera-t-elle pas toujours la première ligne de la méthode du dictionnaire, chaque fois que le dictionnaire sera recréé?
PapillonUK
59
La ligne statique NSDictionary * fooDict = nil; ne sera exécuté qu'une seule fois! Même dans une méthode appelée plusieurs fois, la déclaration (et aussi dans cet exemple l'initialisation) avec le mot-clé static sera ignorée si une variable statique existe avec ce nom.
Binarian
3
@ BenC.R.Leggiero Oui, absolument. La .syntaxe -accessor n'est pas liée aux propriétés dans Objective-C, c'est juste un raccourci compilé pour toute méthode qui renvoie quelque chose sans prendre d'arguments. Dans ce cas, je le préférerais - je préfère personnellement la .syntaxe pour toute utilisation où le code client a l'intention d'obtenir quelque chose, de ne pas effectuer d'action (même si le code d'implémentation peut créer quelque chose une fois ou effectuer des actions avec effets secondaires) . La .syntaxe d' utilisation intensive se traduit également par un code plus lisible: la présence de […]s signifie que quelque chose d'important est en cours lorsque les récupérations utilisent la .syntaxe à la place.
Slipp D. Thompson
4
Jetez un œil à la réponse d'Alex Nolasco, les propriétés de classe sont disponibles depuis la sortie de Xcode 8: stackoverflow.com/a/37849467/6666611
n3wbie
113

J'utilise cette solution:

@interface Model
+ (int) value;
+ (void) setValue:(int)val;
@end

@implementation Model
static int value;
+ (int) value
{ @synchronized(self) { return value; } }
+ (void) setValue:(int)val
{ @synchronized(self) { value = val; } }
@end

Et je l'ai trouvé extrêmement utile en remplacement du motif Singleton.

Pour l'utiliser, accédez simplement à vos données avec la notation par points:

Model.value = 1;
NSLog(@"%d = value", Model.value);
spooki
la source
3
C'est vraiment cool. Mais qu'est-ce que ça selfveut dire à l'intérieur d'une méthode de classe?
Todd Lehman
8
@ToddLehman selfest l'objet qui a reçu le message . Et puisque les classes sont aussi des objets , dans ce cas, cela selfsignifieModel
spooki
6
Pourquoi le getter devrait-il l'être @synchronized?
Matt Kantor
1
C'est en fait assez cool que cela fonctionne. Que vous pouvez utiliser vos propres propriétés de niveau de classe qui fonctionnent exactement comme la vraie chose. Je suppose que la synchronisation de soi équivaut à utiliser 'atomic' dans votre déclaration de propriété et peut être omise si vous voulez la version 'nonatomic'? En outre, je considérerais de nommer les variables de support avec '_' comme c'est le cas par défaut d'Apple et améliore également la lisibilité car le retour / la définition de self.value depuis le getter / setter provoque une récursion infinie. À mon avis, c'est la bonne réponse.
Peter Segerblom
3
C'est cool, mais ... 10 lignes de code juste pour créer 1 membre statique? quel hack. Apple devrait simplement en faire une fonctionnalité.
John Henckel
92

Comme vu dans WWDC 2016 / XCode 8 ( ce qui est nouveau dans la session LLVM @ 5: 05). Les propriétés de classe peuvent être déclarées comme suit

@interface MyType : NSObject
@property (class) NSString *someString;
@end

NSLog(@"format string %@", MyType.someString);

Notez que les propriétés de classe ne sont jamais synthétisées

@implementation
static NSString * _someString;
+ (NSString *)someString { return _someString; }
+ (void)setSomeString:(NSString *)newString { _someString = newString; }
@end
Alex Nolasco
la source
8
Il faudrait peut-être préciser que ce n'est que du sucre pour les déclarations des accesseurs. Comme vous l'avez noté, la propriété n'est pas synthétisée: une staticvariable ( ) doit toujours être déclarée et utilisée, et les méthodes implémentées explicitement, telles qu'elles étaient auparavant. La syntaxe des points fonctionnait déjà auparavant également. Dans l'ensemble, cela semble être une affaire plus importante qu'elle ne l'est vraiment.
jscs
6
Le gros problème est que cela signifie que vous pouvez accéder aux singletons à partir du code Swift sans utiliser (), et le suffixe de type est supprimé par convention. Par exemple, XYZMyClass.shared (Swift 3) au lieu de XYZMyClass.sharedMyClass ()
Ryan
Cela ne semble pas sûr pour les threads. Si cela pouvait être muté à partir de différents threads dans votre code, je m'assurerais de gérer la condition de concurrence potentielle que cela créerait.
smileBot
Une belle interface propre pour consommer des classes mais c'est toujours une tonne de travail. J'ai essayé cela avec un bloc statique et ce n'était pas très amusant. En fait, l'utilisation de l'électricité statique était beaucoup plus facile.
Departamento B du
1
l'implémentation peut être facilement améliorée pour un comportement "singleton" thread-safe, en utilisant le jeton dispatch_once - mais sinon la solution montre correctement la réponse
Motti Shneor
63

Si vous recherchez l'équivalent de niveau classe @property, alors la réponse est "il n'y a rien de tel". Mais rappelez-vous, @propertyn'est que du sucre syntaxique, de toute façon; il crée simplement des méthodes d'objet correctement nommées.

Vous voulez créer des méthodes de classe qui accèdent à des variables statiques qui, comme d'autres l'ont dit, n'ont qu'une syntaxe légèrement différente.

Jim Puls
la source
Même les propriétés de pensée sont syntaxiques. Il serait toujours agréable de pouvoir utiliser la syntaxe à points pour des choses comme MyClass.class au lieu de [classe MyClass].
Zaky German
4
@ZakyGerman Vous pouvez! UIDevice.currentDevice.identifierForVendortravaille pour moi.
tc.
1
@tc. Je vous remercie! Remplir idiot maintenant. Pour une raison quelconque, j'étais convaincu que j'avais essayé cela dans le passé, mais cela n'a pas fonctionné. Est-ce une nouvelle fonctionnalité par hasard?
Zaky German
1
@ZakyGerman Cela a fonctionné pendant au moins un an ou deux pour les méthodes de classe. Je crois que cela a toujours fonctionné pour les méthodes d'instance si les méthodes getter / setter ont les types attendus.
tc.
21

Voici un moyen sûr de le faire:

// Foo.h
@interface Foo {
}

+(NSDictionary*) dictionary;

// Foo.m
+(NSDictionary*) dictionary
{
  static NSDictionary* fooDict = nil;

  static dispatch_once_t oncePredicate;

  dispatch_once(&oncePredicate, ^{
        // create dict
    });

  return fooDict;
}

Ces modifications garantissent que fooDict n'est créé qu'une seule fois.

À partir de la documentation Apple : "dispatch_once - Exécute un objet bloc une seule fois pour la durée de vie d'une application."

Quentin
la source
3
Le code dispatch_once n'est-il pas inutile, car un NSDictionary statique pourrait être initialisé sur la première ligne de la méthode du dictionnaire + (NSDictionary *) et comme il est statique, il ne sera de toute façon initialisé qu'une seule fois?
jcpennypincher
@jcpennypincher tentative d'initialiser le dictionnaire sur la même ligne que sa déclaration statique donne l'erreur du compilateur suivant: Initializer element is not a compile-time constant.
George WS
@GeorgeWS Vous n'obtenez cette erreur que parce que vous essayez de l'initialiser au résultat d'une fonction (alloc et init sont des fonctions). Si vous l'initialisez à zéro, puis ajoutez le if (obj == nil) et initialisez-le, tout ira bien.
Rob
1
Rob, ce n'est pas sûr pour les threads. Le code présenté ici est le meilleur.
Ian Ollmann
J'aime mieux cette réponse, car elle est complète et sûre pour les threads - cependant, elle ne traite que mieux de l'implémentation, alors que la question de l'op concernait la déclaration des propriétés de classe - pas leur implémentation (qui n'a rien à voir avec l'implémentation). Merci quand même.
Motti Shneor
11

Depuis Xcode 8, Objective-C prend désormais en charge les propriétés de classe:

@interface MyClass : NSObject
@property (class, nonatomic, assign, readonly) NSUUID* identifier;
@end

Comme les propriétés de classe ne sont jamais synthétisées, vous devez écrire votre propre implémentation.

@implementation MyClass
static NSUUID*_identifier = nil;

+ (NSUUID *)identifier {
  if (_identifier == nil) {
    _identifier = [[NSUUID alloc] init];
  }
  return _identifier;
}
@end

Vous accédez aux propriétés de la classe à l'aide de la syntaxe à points normale sur le nom de la classe:

MyClass.identifier;
berbie
la source
7

Les propriétés ont des valeurs uniquement dans les objets, pas dans les classes.

Si vous devez stocker quelque chose pour tous les objets d'une classe, vous devez utiliser une variable globale. Vous pouvez le cacher en le déclarantstatic dans le fichier d'implémentation.

Vous pouvez également envisager d'utiliser des relations spécifiques entre vos objets: vous attribuez un rôle de maître à un objet spécifique de votre classe et liez d'autres objets à ce maître. Le maître conservera le dictionnaire comme une simple propriété. Je pense à un arbre comme celui utilisé pour la hiérarchie des vues dans les applications Cocoa.

Une autre option consiste à créer un objet d'une classe dédiée qui est composé à la fois de votre dictionnaire «classe» et d'un ensemble de tous les objets liés à ce dictionnaire. C'est quelque chose comme NSAutoreleasePooldans Cocoa.

mouviciel
la source
7

À partir de Xcode 8, vous pouvez utiliser l' attribut de propriété de classe comme l'a répondu Berbie.

Cependant, dans l'implémentation, vous devez définir à la fois le getter de classe et le setter pour la propriété de classe à l'aide d'une variable statique au lieu d'un iVar.

Sample.h

@interface Sample: NSObject
@property (class, retain) Sample *sharedSample;
@end

Sample.m

@implementation Sample
static Sample *_sharedSample;
+ ( Sample *)sharedSample {
   if (_sharedSample==nil) {
      [Sample setSharedSample:_sharedSample];
   }
   return _sharedSample;
}

+ (void)setSharedSample:(Sample *)sample {
   _sharedSample = [[Sample alloc]init];
}
@end
Jean-Marie D.
la source
2

Si vous avez de nombreuses propriétés de niveau classe, un modèle singleton peut être en ordre. Quelque chose comme ça:

// Foo.h
@interface Foo

+ (Foo *)singleton;

@property 1 ...
@property 2 ...
@property 3 ...

@end

Et

// Foo.m

#import "Foo.h"

@implementation Foo

static Foo *_singleton = nil;

+ (Foo *)singleton {
    if (_singleton == nil) _singleton = [[Foo alloc] init];

    return _singleton;
}

@synthesize property1;
@synthesize property2;
@synthesise property3;

@end

Accédez maintenant à vos propriétés au niveau de la classe comme ceci:

[Foo singleton].property1 = value;
value = [Foo singleton].property2;
Pedro Borges
la source
4
Cette implémentation singleton n'est pas sûre pour les threads, ne l'utilisez pas
klefevre
1
Bien sûr, il n'est pas sûr pour les threads, vous êtes la première personne à mentionner la sécurité des threads et par défaut n'a aucun sens la surcharge de sécurité des threads dans des contextes non thread-safe, c'est-à-dire un seul thread.
Pedro Borges
Il serait assez facile d'utiliser un dispatch_onceici.
Ian MacDonald
L'OP voulait une réponse du côté de la Déclaration, pas la mise en œuvre - et même la mise en œuvre suggérée est incomplète (pas thread-safe).
Motti Shneor
-3

[Essayez cette solution, c'est simple] Vous pouvez créer une variable statique dans une classe Swift puis l'appeler à partir de n'importe quelle classe Objective-C.

jawad
la source
1
OP n'a pas demandé comment créer une propriété statique dans Swift, cela ne résout pas son problème.
Nathan F.
Ou plutôt, cela a résolu le problème, mais n'a pas répondu à la question. Je ne sais pas si cela mérite et voter contre ...
AmitaiB