web-dev-qa-db-fra.com

Constantes en Objective-C

Je développe une application Cocoa et j'utilise des variables NSStrings constantes pour stocker les noms de clé correspondant à mes préférences.

Je comprends que c'est une bonne idée, car cela permet de changer facilement les clés si nécessaire. De plus, il s’agit de l’ensemble de la notion de «séparation de vos données de votre logique».

Quoi qu'il en soit, existe-t-il un bon moyen de définir ces constantes une fois pour toute l'application? Je suis sûr qu'il existe un moyen simple et intelligent, mais pour le moment, mes cours ne font que redéfinir ceux qu'ils utilisent. 

981
Allyn

Vous devriez créer un fichier d’en-tête comme

// Constants.h
FOUNDATION_EXPORT NSString *const MyFirstConstant;
FOUNDATION_EXPORT NSString *const MySecondConstant;
//etc.

(vous pouvez utiliser extern au lieu de FOUNDATION_EXPORT si votre code ne sera pas utilisé dans des environnements mixtes C/C++ ou sur d'autres plates-formes)

Vous pouvez inclure ce fichier dans chaque fichier utilisant les constantes ou dans l'en-tête précompilé du projet. 

Vous définissez ces constantes dans un fichier .m comme

// Constants.m
NSString *const MyFirstConstant = @"FirstConstant";
NSString *const MySecondConstant = @"SecondConstant";

Constants.m doit être ajouté à la cible de votre application/infrastructure afin qu’il soit lié au produit final.

L'avantage d'utiliser des constantes de chaîne au lieu de constantes #define 'd est que vous pouvez tester l'égalité en utilisant la comparaison de pointeur (stringInstance == MyFirstConstant), qui est beaucoup plus rapide que la comparaison de chaîne ([stringInstance isEqualToString:MyFirstConstant]) (et plus facile à lire, IMO).

1262
Barry Wark

Manière la plus simple:

// Prefs.h
#define PREFS_MY_CONSTANT @"prefs_my_constant"

Meilleure façon:

// Prefs.h
extern NSString * const PREFS_MY_CONSTANT;

// Prefs.m
NSString * const PREFS_MY_CONSTANT = @"prefs_my_constant";

Un des avantages de la seconde est que changer la valeur d'une constante ne provoque pas une reconstruction de l'ensemble de votre programme.

270
Andrew Grant

Il y a aussi une chose à mentionner. Si vous avez besoin d'une constante non globale, vous devez utiliser le mot clé static

Exemple

// In your *.m file
static NSString * const kNSStringConst = @"const value";

En raison du mot clé static, cette constante n'est pas visible en dehors du fichier.


Correction mineure de @QuinnTaylor : les variables statiques sont visibles dans une unité de compilation. Il s’agit généralement d’un fichier .m unique (comme dans cet exemple), mais il peut vous mordre si vous le déclarez dans un en-tête inclus ailleurs, car vous obtiendrez des erreurs de l'éditeur de liens après la compilation

184
kompozer

La réponse acceptée (et correcte) indique que "vous pouvez inclure ce fichier [Constants.h] ... dans l'en-tête précompilé du projet".

En tant que novice, j’ai eu du mal à le faire sans plus d'explications - voici comment: Dans votre fichier YourAppNameHere-Prefix.pch (nom par défaut de l'en-tête précompilé dans Xcode), importez votre Constants.h dans le bloc #ifdef __OBJC__.

#ifdef __OBJC__
  #import <UIKit/UIKit.h>
  #import <Foundation/Foundation.h>
  #import "Constants.h"
#endif

Notez également que les fichiers Constants.h et Constants.m ne doivent contenir absolument rien d’autre, à l’exception de ce qui est décrit dans la réponse acceptée. (Pas d'interface ou d'implémentation).

117
Victor Van Hee

J'utilise généralement la manière indiquée par Barry Wark et Rahul Gupta.

Bien que je n'aime pas répéter les mêmes mots dans les fichiers .h et .m . Notez que, dans l'exemple suivant, la ligne est presque identique dans les deux fichiers:

// file.h
extern NSString* const MyConst;

//file.m
NSString* const MyConst = @"Lorem ipsum";

Par conséquent, ce que j'aime faire, c’est utiliser des machines de préprocesseur en C ………… Permettez-moi de vous expliquer à travers cet exemple.

J'ai un fichier d'en-tête qui définit la macro STR_CONST(name, value):

// StringConsts.h
#ifdef SYNTHESIZE_CONSTS
# define STR_CONST(name, value) NSString* const name = @ value
#else
# define STR_CONST(name, value) extern NSString* const name
#endif

Le dans ma paire .h/.m où je veux définir la constante, je fais ce qui suit:

// myfile.h
#import <StringConsts.h>

STR_CONST(MyConst, "Lorem Ipsum");
STR_CONST(MyOtherConst, "Hello world");

// myfile.m
#define SYNTHESIZE_CONSTS
#import "myfile.h"

et voila, j'ai toutes les informations sur les constantes dans le fichier .h seulement. 

50
Krizz

Une légère modification de la suggestion de @Krizz pour qu'elle fonctionne correctement si le fichier d'en-tête des constantes doit être inclus dans PCH, ce qui est plutôt normal. Étant donné que l'original est importé dans PCH, il ne sera pas rechargé dans le fichier .m. Vous n'obtenez donc aucun symbole et l'éditeur de liens est malheureux.

Cependant, la modification suivante lui permet de fonctionner. C'est un peu compliqué, mais ça marche.

Vous aurez besoin de 3 files, fichier .h qui contient les définitions constantes, le fichier .h et le fichier .m. Je vais utiliser ConstantList.h, Constants.h et Constants.m, respectivement. le contenu de Constants.h est simplement:

// Constants.h
#define STR_CONST(name, value) extern NSString* const name
#include "ConstantList.h"

et le fichier Constants.m ressemble à:

// Constants.m
#ifdef STR_CONST
    #undef STR_CONST
#endif
#define STR_CONST(name, value) NSString* const name = @ value
#include "ConstantList.h"

Enfin, le fichier ConstantList.h contient les déclarations réelles et c’est tout:

// ConstantList.h
STR_CONST(kMyConstant, "Value");
…

Quelques points à noter:

  1. Je devais redéfinir la macro dans le fichier .m après #undefing pour que la macro soit utilisée.

  2. Je devais également utiliser#includeau lieu de #import pour que cela fonctionne correctement et éviter que le compilateur ne voie les valeurs précédemment précompilées.

  3. Cela nécessitera une recompilation de votre PCH (et probablement de l'ensemble du projet) chaque fois que des valeurs sont modifiées, ce qui n'est pas le cas si elles sont séparées (et dupliquées) normalement.

J'espère que c'est utile pour quelqu'un.

25
Scott Little

J'ai moi-même un en-tête dédié à la déclaration de NSStrings constantes utilisées pour les préférences comme ceci:

extern NSString * const PPRememberMusicList;
extern NSString * const PPLoadMusicAtListLoad;
extern NSString * const PPAfterPlayingMusic;
extern NSString * const PPGotoStartupAfterPlaying;

Puis en les déclarant dans le fichier .m joint: 

NSString * const PPRememberMusicList = @"Remember Music List";
NSString * const PPLoadMusicAtListLoad = @"Load music when loading list";
NSString * const PPAfterPlayingMusic = @"After playing music";
NSString * const PPGotoStartupAfterPlaying = @"Go to startup pos. after playing";

Cette approche m'a bien servi.

Edit: Notez que cela fonctionne mieux si les chaînes sont utilisées dans plusieurs fichiers. Si un seul fichier l'utilise, vous pouvez simplement faire #define kNSStringConstant @"Constant NSString" dans le fichier .m qui utilise la chaîne.

25
MaddTheSane
// Prefs.h
extern NSString * const RAHUL;

// Prefs.m
NSString * const RAHUL = @"rahul";
14
rahul gupta

Comme Abizer l'a dit, vous pouvez le mettre dans le fichier PCH. Une autre méthode moins sale consiste à créer un fichier include pour toutes vos clés, puis à l'inclure dans le fichier dans lequel vous utilisez les clés ou à l'inclure dans PCH. Avec eux dans leur propre fichier d’inclusion, vous disposez au moins d’un endroit où rechercher et définir toutes ces constantes.

12
Grant Limberg

Si vous voulez quelque chose comme des constantes globales; un moyen rapide et sale consiste à placer les déclarations de constante dans le fichier pch.

11
Abizern

Essayez d'utiliser une méthode de classe:

+(NSString*)theMainTitle
{
    return @"Hello World";
}

Je l'utilise parfois.

8
groumpf

Si vous aimez la constante d'espace de nom, vous pouvez utiliser struct, Friday Q & R 2011-08-19: Constantes et fonctions de namespaced

// in the header
extern const struct MANotifyingArrayNotificationsStruct
{
    NSString *didAddObject;
    NSString *didChangeObject;
    NSString *didRemoveObject;
} MANotifyingArrayNotifications;

// in the implementation
const struct MANotifyingArrayNotificationsStruct MANotifyingArrayNotifications = {
    .didAddObject = @"didAddObject",
    .didChangeObject = @"didChangeObject",
    .didRemoveObject = @"didRemoveObject"
};
8
onmyway133

J'utilise une classe singleton, afin de pouvoir me moquer de la classe et de changer les constantes si nécessaire pour les tests. La classe des constantes ressemble à ceci:

#import <Foundation/Foundation.h>

@interface iCode_Framework : NSObject

@property (readonly, nonatomic) unsigned int iBufCapacity;
@property (readonly, nonatomic) unsigned int iPort;
@property (readonly, nonatomic) NSString * urlStr;

@end

#import "iCode_Framework.h"

static iCode_Framework * instance;

@implementation iCode_Framework

@dynamic iBufCapacity;
@dynamic iPort;
@dynamic urlStr;

- (unsigned int)iBufCapacity
{
    return 1024u;
};

- (unsigned int)iPort
{
    return 1978u;
};

- (NSString *)urlStr
{
    return @"localhost";
};

+ (void)initialize
{
    if (!instance) {
        instance = [[super allocWithZone:NULL] init];
    }
}

+ (id)allocWithZone:(NSZone * const)notUsed
{
    return instance;
}

@end

Et il est utilisé comme ceci (notez l’utilisation d’un raccourci pour les constantes c - cela enregistre la saisie de [[Constants alloc] init] à chaque fois):

#import "iCode_FrameworkTests.h"
#import "iCode_Framework.h"

static iCode_Framework * c; // Shorthand

@implementation iCode_FrameworkTests

+ (void)initialize
{
    c  = [[iCode_Framework alloc] init]; // Used like normal class; easy to mock!
}

- (void)testSingleton
{
    STAssertNotNil(c, nil);
    STAssertEqualObjects(c, [iCode_Framework alloc], nil);
    STAssertEquals(c.iBufCapacity, 1024u, nil);
}

@end
7
Howard Lovatt

Si vous voulez appeler quelque chose comme ceci NSString.newLine; depuis Objective C et que vous voulez que ce soit une constante statique, vous pouvez créer quelque chose comme ceci dans Swift:

public extension NSString {
    @objc public static let newLine = "\n"
}

Et vous avez une définition de Nice lisible et constante, et disponible dans le type de votre choix tout en restant liée au contexte du type.

0
Renetik