Variables de niveau de classe statique Objective-C

143

J'ai un film de classe, dont chacun stocke un identifiant unique. En C #, Java, etc., je peux définir un currentID int statique et chaque fois que je définis l'ID, je peux augmenter le currentID et le changement se produit au niveau de la classe et non au niveau de l'objet. Cela peut-il être fait en Objective-C? J'ai trouvé très difficile de trouver une réponse à cela.

Cœur
la source

Réponses:

158

Description du problème :

  1. Vous voulez que votre ClassA ait une variable de classe ClassB.
  2. Vous utilisez Objective-C comme langage de programmation.
  3. Objective-C ne prend pas en charge les variables de classe comme le fait C ++.

Une alternative :

Simuler le comportement d'une variable de classe à l'aide des fonctionnalités Objective-C

  1. Déclarez / définissez une variable statique dans le classA.m afin qu'elle ne soit accessible que pour les méthodes classA (et tout ce que vous mettez dans classA.m).

  2. Remplacez la méthode de classe d'initialisation NSObject pour initialiser une seule fois la variable statique avec une instance de ClassB.

  3. Vous vous demanderez pourquoi devrais-je écraser la méthode d'initialisation de NSObject. La documentation Apple sur cette méthode a la réponse: "Le runtime envoie initialize à chaque classe d'un programme exactement une fois juste avant que la classe, ou toute classe qui en hérite, reçoive son premier message depuis le programme. (Ainsi, la méthode ne peut jamais être invoquée si la classe n'est pas utilisée.) ".

  4. N'hésitez pas à utiliser la variable statique dans n'importe quelle méthode de classe / instance ClassA.

Exemple de code :

fichier: classA.m

static ClassB *classVariableName = nil;

@implementation ClassA

...
 
+(void) initialize
{
    if (! classVariableName)
        classVariableName = [[ClassB alloc] init];
}

+(void) classMethodName
{
    [classVariableName doSomething]; 
}

-(void) instanceMethodName
{
    [classVariableName doSomething]; 
}

...

@end

Références :

  1. Variables de classe expliquées en comparant les approches Objective-C et C ++
Albaregar
la source
3
Pouvez-vous avoir une variable statique de type ClassA dans classA.m?
goatlinks
6
cela pourrait être une question idiote, mais qu'en est-il de la libération dudit souvenir? n'a pas d'importance car il doit vivre aussi longtemps que l'application est en cours d'exécution?
samiq
1
@samiq, consultez Objective-C: Pourquoi conserver une variable statique? . Le pointeur vers l'objet ne peut pas être supprimé, mais l'objet lui-même le peut. Vous ne voulez probablement pas le publier parce que vous le souhaitez probablement aussi longtemps que l'application est en cours d'exécution, mais vous économiserez de la mémoire si vous le libérez, donc si vous savez que vous n'en avez plus besoin, alors vous devriez Publiez-le.
ma11hew28
5
Si initialize () est garanti pour être appelé une seule fois, pourquoi avez-vous besoin du conditionnel "if (! ClassVariableName)"?
jb
23
@jamie, initializeest appelée une fois pour chaque classe (les superclasses avant les sous-classes), mais si une sous-classe ne remplace pas initialize, la classe parente initializesera à nouveau appelée. Par conséquent, une garde est requise si vous ne voulez pas que ce code s'exécute deux fois. Consultez Initialisation d'un objet de classe dans la documentation Objective-C d'Apple.
big_m
31

Depuis Xcode 8, vous pouvez définir les propriétés de classe dans Obj-C. Cela a été ajouté pour interagir avec les propriétés statiques de Swift.

Objective-C prend désormais en charge les propriétés de classe, qui interagissent avec les propriétés de type Swift. Ils sont déclarés comme: @property (class) NSString * someStringProperty ;. Ils ne sont jamais synthétisés. (23891898)

Voici un exemple

@interface YourClass : NSObject

@property (class, nonatomic, assign) NSInteger currentId;

@end

@implementation YourClass

static NSInteger _currentId = 0;

+ (NSInteger)currentId {
    return _currentId;
}

+ (void)setCurrentId:(NSInteger)newValue {
    _currentId = newValue;
}

@end

Ensuite, vous pouvez y accéder comme ceci:

YourClass.currentId = 1;
val = YourClass.currentId;

Voici un article explicatif très intéressant que j'ai utilisé comme référence pour éditer cette ancienne réponse.


Réponse de 2011: (ne l'utilisez pas, c'est terrible)

Si vous ne voulez vraiment pas déclarer une variable globale, il existe une autre option, peut-être pas très orthodoxe :-), mais qui fonctionne ... Vous pouvez déclarer une méthode "get & set" comme celle-ci, avec une variable statique à l'intérieur:

+ (NSString*)testHolder:(NSString*)_test {
    static NSString *test;

    if(_test != nil) {
        if(test != nil)
            [test release];
        test = [_test retain];
    }

    // if(test == nil)
    //     test = @"Initialize the var here if you need to";

    return test;
}

Donc, si vous avez besoin d'obtenir la valeur, appelez simplement:

NSString *testVal = [MyClass testHolder:nil]

Et puis, lorsque vous souhaitez le paramétrer:

[MyClass testHolder:testVal]

Dans le cas où vous voulez pouvoir définir cette pseudo-statique-var à nil, vous pouvez déclarer testHoldercomme ceci:

+ (NSString*)testHolderSet:(BOOL)shouldSet newValue:(NSString*)_test {
    static NSString *test;

    if(shouldSet) {
        if(test != nil)
            [test release];
        test = [_test retain];
    }

    return test;
}

Et deux méthodes pratiques:

+ (NSString*)test {
    return [MyClass testHolderSet:NO newValue:nil];
}

+ (void)setTest:(NSString*)_test {
    [MyClass testHolderSet:YES newValue:_test];
}

J'espère que ça aide! Bonne chance.

Gonzalo Larralde
la source
Cool, mais ce n'est pas vraiment une variable globale car elle n'est pas accessible à partir d'autres .mfichiers, et je pense que c'est bien qu'elle soit "globale" dans le Class.mfichier.
ma11hew28
29

Sur votre fichier .m, vous pouvez déclarer une variable comme statique:

static ClassName *variableName = nil;

Ensuite, vous pouvez l'initialiser sur votre +(void)initializeméthode.

Veuillez noter qu'il s'agit d'une simple variable statique C et qu'elle n'est pas statique dans le sens où Java ou C # le considèrent, mais donnera des résultats similaires.

pgb
la source
16

Dans votre fichier .m, déclarez une variable globale de fichier:

static int currentID = 1;

puis dans votre routine d'initialisation, repérez que:

- (id) init
{
    self = [super init];
    if (self != nil) {
        _myID = currentID++; // not thread safe
    }
    return self;
}

ou s'il doit changer à un autre moment (par exemple dans votre méthode openConnection), incrémentez-le là. N'oubliez pas que ce n'est pas thread-safe tel quel, vous devrez faire la synchronisation (ou mieux encore, utiliser un ajout atomique) s'il y a des problèmes de thread.

Peter N Lewis
la source
11

Comme pgb l'a dit, il n'y a pas de "variables de classe", seulement des "variables d'instance". La manière objective-c de faire des variables de classe est une variable globale statique à l'intérieur du fichier .m de la classe. Le "statique" garantit que la variable ne peut pas être utilisée en dehors de ce fichier (c'est-à-dire qu'elle ne peut pas être externe).

Tom Dalling
la source
3

Voici une option:

+(int)getId{
    static int id;
    //Do anything you need to update the ID here
    return id;
}

Notez que cette méthode sera la seule méthode pour accéder à id, vous devrez donc la mettre à jour d'une manière ou d'une autre dans ce code.

Anonyme
la source
2

(À proprement parler, pas de réponse à la question, mais selon mon expérience, susceptible d'être utile lors de la recherche de variables de classe)

Une méthode de classe peut souvent jouer plusieurs des rôles qu'une variable de classe ferait dans d'autres langues (par exemple, modification de la configuration pendant les tests):

@interface MyCls: NSObject
+ (NSString*)theNameThing;
- (void)doTheThing;
@end
@implementation
+ (NSString*)theNameThing { return @"Something general"; }
- (void)doTheThing {
  [SomeResource changeSomething:[self.class theNameThing]];
}
@end

@interface MySpecialCase: MyCls
@end
@implementation
+ (NSString*)theNameThing { return @"Something specific"; }
@end

Maintenant, un objet de classe MyClsappelle Resource:changeSomething:avec la chaîne @"Something general"lors d'un appel à doTheThing:, mais un objet dérivé de MySpecialCaseavec la chaîne @"Something specific".

Jacob Oscarson
la source
0

Vous pouvez renommer la classe en classA.mm et y ajouter des fonctionnalités C ++.

rd_
la source
0

Une autre possibilité serait d'avoir un petit NSNumbersingleton de sous-classe.

Rudolf Adamkovič
la source