it-swarm.com.de

Konstanten in Objective-C

Ich entwickle eine Cocoa - Anwendung und verwende Konstanten NSStrings, um Schlüsselnamen für meine Vorlieben zu speichern.

Ich verstehe, dass dies eine gute Idee ist, da sie bei Bedarf ein einfaches Wechseln der Tasten ermöglicht. Darüber hinaus ist es die ganze Idee, Ihre Daten von Ihrer Logik zu trennen.

Gibt es trotzdem eine gute Möglichkeit, diese Konstanten einmal für die gesamte Anwendung zu definieren? Ich bin sicher, dass es einen einfachen und intelligenten Weg gibt, aber im Moment definieren meine Klassen nur die, die sie verwenden. 

981
Allyn

Sie sollten eine Header-Datei wie erstellen

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

(Sie können extern anstelle von FOUNDATION_EXPORT verwenden, wenn Ihr Code nicht in gemischten C/C++ - Umgebungen oder auf anderen Plattformen verwendet wird.)

Sie können diese Datei in jede Datei einschließen, die die Konstanten verwendet, oder in den vorkompilierten Header des Projekts. 

Sie definieren diese Konstanten wie in einer .m-Datei

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

Constants.m sollte zum Ziel Ihrer Anwendung/des Frameworks hinzugefügt werden, damit es mit dem Endprodukt verknüpft wird.

Der Vorteil der Verwendung von String-Konstanten anstelle von #defined-Konstanten besteht darin, dass Sie die Gleichheit mit Hilfe des Zeigervergleichs (stringInstance == MyFirstConstant) testen können, der viel schneller ist als der Zeichenkettenvergleich ([stringInstance isEqualToString:MyFirstConstant]) (und einfacher zu lesen, IMO).

1262
Barry Wark

Einfachster Weg:

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

Besserer Weg:

// Prefs.h
extern NSString * const PREFS_MY_CONSTANT;

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

Der zweite Vorteil ist, dass das Ändern des Werts einer Konstante nicht dazu führt, dass das gesamte Programm neu erstellt wird.

270
Andrew Grant

Es gibt auch eine Sache zu erwähnen. Wenn Sie eine nicht globale Konstante benötigen, sollten Sie das Schlüsselwort static verwenden. 

Beispiel

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

Aufgrund des Schlüsselworts static ist diese Konstante außerhalb der Datei nicht sichtbar.


Kleinere Korrekturen mit @QuinnTaylor : Statische Variablen sind innerhalb einer Compilation Unit sichtbar. Normalerweise handelt es sich hierbei um eine einzelne .m-Datei (wie in diesem Beispiel), die Sie jedoch beißen kann, wenn Sie sie in einem an anderer Stelle enthaltenen Header deklarieren, da nach der Kompilierung Linker-Fehler auftreten

184
kompozer

Die akzeptierte (und korrekte) Antwort lautet "Sie können diese [Constants.h] -Datei ... in den vorkompilierten Header für das Projekt einfügen."

Als Neuling hatte ich Schwierigkeiten, dies ohne weitere Erklärung zu tun. So wird's gemacht: Importieren Sie in Ihrer YourAppNameHere-Prefix.pch-Datei (dies ist der Standardname für den vorkompilierten Header in Xcode) Ihre Constants.h im Block #ifdef __OBJC__.

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

Beachten Sie außerdem, dass die Dateien Constants.h und Constants.m nichts anderes enthalten dürfen als das, was in der akzeptierten Antwort beschrieben wird. (Keine Schnittstelle oder Implementierung).

117
Victor Van Hee

Ich benutze im Allgemeinen den von Barry Wark und Rahul Gupta geposteten Weg.

Ich mag es zwar nicht, dieselben Wörter in der .h- und .m-Datei zu wiederholen Beachten Sie, dass im folgenden Beispiel die Zeile in beiden Dateien fast identisch ist:

// file.h
extern NSString* const MyConst;

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

Was ich also gerne mache, ist die Verwendung einer C-Präprozessor-Maschine.

Ich habe eine Header-Datei, die das Makro STR_CONST(name, value) definiert:

// 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

In meinem .h/.m-Paar, in dem ich die Konstante definieren möchte, mache ich Folgendes:

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

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

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

ich habe alle Informationen über die Konstanten nur in der .h-Datei. 

50
Krizz

Eine geringfügige Änderung des Vorschlags von @Krizz, so dass es richtig funktioniert, wenn die Konstanten-Header-Datei in den PCH aufgenommen werden soll, was ziemlich normal ist. Da das Original in den PCH importiert wird, wird es nicht erneut in die .m-Datei geladen. Dadurch werden keine Symbole angezeigt, und der Linker ist unglücklich.

Die folgende Änderung ermöglicht jedoch die Arbeit. Es ist etwas kompliziert, aber es funktioniert.

Du brauchst 3 files, .h-Datei, die die konstanten Definitionen hat, die .h-Datei und die .m-Datei. Ich verwende ConstantList.h, Constants.h und Constants.m. Der Inhalt von Constants.h ist einfach:

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

und die Constants.m-Datei sieht folgendermaßen aus:

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

Schließlich enthält die ConstantList.h-Datei die eigentlichen Deklarationen, und das ist alles:

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

Ein paar Dinge zu beachten:

  1. Ich musste das Makro in der .m-Datei after #undefing neu definieren, damit das Makro verwendet werden kann.

  2. Ich musste auch#includeanstelle von #import verwenden, damit dies ordnungsgemäß funktioniert und vermieden wird, dass der Compiler die zuvor vorkompilierten Werte sieht.

  3. Dies erfordert eine Neukompilierung Ihres PCHs (und wahrscheinlich des gesamten Projekts), wenn Werte geändert werden. Dies ist nicht der Fall, wenn sie wie üblich getrennt (und dupliziert) werden.

Hoffe das ist für jemanden hilfreich.

25
Scott Little

Ich selbst habe einen Header, der dazu dient, konstante NSStrings für Präferenzen zu deklarieren:

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

Dann deklarieren Sie sie in der zugehörigen .m-Datei: 

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";

Dieser Ansatz hat mir gut geholfen.

Bearbeiten: Beachten Sie, dass dies am besten funktioniert, wenn die Zeichenfolgen in mehreren Dateien verwendet werden. Wenn nur eine Datei es verwendet, können Sie einfach #define kNSStringConstant @"Constant NSString" in der .m-Datei tun, die die Zeichenfolge verwendet.

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

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

Wie Abizer gesagt hat, könnte man es in die PCH-Datei schreiben. Eine andere Möglichkeit, die nicht so schmutzig ist, besteht darin, eine Include-Datei für alle Ihre Schlüssel zu erstellen und diese entweder in die Datei aufzunehmen, in der Sie die Schlüssel verwenden, oder sie in den PCH aufzunehmen. Mit ihnen in ihrer eigenen Include-Datei, die Ihnen zumindest eine Stelle gibt, an der Sie alle diese Konstanten suchen und definieren können.

12
Grant Limberg

Wenn Sie so etwas wie globale Konstanten wollen; Eine schnelle und schmutzige Methode besteht darin, die konstanten Deklarationen in die pch-Datei zu schreiben.

11
Abizern

Versuchen Sie es mit einer Klassenmethode:

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

Ich benutze es manchmal.

8
groumpf

Wenn Sie die Namespace-Konstante mögen, können Sie struct, Friday Q & A 2011-08-19 verwenden: Konstanten und Funktionen mit Namensraum

// 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

Ich benutze eine Singleton-Klasse, um die Klasse zu verspotten und die Konstanten zu ändern, falls dies zum Testen erforderlich ist. Die Konstantenklasse sieht so aus:

#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

Und es wird wie folgt verwendet (Beachten Sie die Verwendung einer Abkürzung für die Konstanten c - es erspart sich jedes Mal die Eingabe von [[Constants alloc] init]):

#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

Wenn Sie etwas wie NSString.newLine; von Objective C aufrufen möchten und es statisch konstant sein soll, können Sie in Swift Folgendes erstellen:

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

Und Sie haben eine Nizza-lesbare konstante Definition und sind innerhalb eines Typs Ihrer Wahl verfügbar, während der Stil an den Kontext des Typs gebunden ist.

0
Renetik