2009-06-29 5 views
16

Ich möchte eine Konstante in objective-c definieren.Eine Konstante in objective-c definieren

Vorher hatte ich die folgende Funktion:

+(NSString *) getDocumentsDir { 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES); 
    NSString *documentsDir = [paths objectAtIndex: 0]; 
    paths = nil; 
    return documentsDir; 
} 

Ich mag würde nur einmal eine Konstante „Documents_Dir“ definieren - wenn die Funktion und danach aufgerufen wird, um ein zuvor erstelltes Wert zuzugreifen.

Ich habe den folgenden Code versucht, die nicht funktioniert hat:

#define getDocumentsDir \ 
{ \ 
#ifdef Documents_Dir \ 
return Documents_Dir; \ 
#else \ 
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES); \ 
NSString *documentsDir = [paths objectAtIndex: 0]; \ 
#define Documents_Dir [paths objectAtIndex: 0]; \ 
paths = nil; \ 
return Documents_Dir; \ 
#endif \ 
} \ 

Ich bin nicht stark mit Precompiler-Richtlinien, so wird jede Hilfe dankbar.

Antwort

34

Vorwort: Es lohnt sich, den Unterschied zwischen Precompiler-Direktiven und wahren Konstanten zu verstehen. A #define führt nur eine Textersetzung durch, bevor der Compiler den Code erstellt. Dies funktioniert hervorragend für numerische Konstanten und typedefs, ist aber nicht immer die beste Idee für Funktions- oder Methodenaufrufe. Ich gehe davon aus, dass Sie wirklich eine echte Konstante haben wollen, dh der Code zum Erstellen des Suchpfades sollte nur einmal ausgeführt werden.


In Ihrer MyClass.m Datei, definieren Sie die Variable und füllt es in einer +initialize Methode wie folgt:

static NSArray *documentsDir; 

@implementation MyClass 

+ (void) initialize { 
    if (documentsDir == nil) { 
     documentsDir = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES) lastObject] retain]; 
    } 
} 

... 

@end 

Der static Modifikator macht es sichtbar nur in der Zusammenstellung Einheit, wo es erklärt wird. Für eine einfache Konstante ist das alles, was Sie brauchen.

Wenn die Klasse Unterklassen hat, wird +initialize für jede Unterklasse einmal aufgerufen werden (Standardeinstellung), so dass Sie überprüfen wollen werden, ob documentsDir ist nil vor ihm zuweisen, so dass Sie nicht Speicher dicht sind. (Oder, wie Peter Lewis hervorhebt, können Sie überprüfen, ob die gerade initialisierte Klasse die MyClass ist, entweder == oder die -isMemberOfClass:-Methode.) Wenn die Unterklassen auch direkt auf die Konstante zugreifen müssen, müssen Sie sie vorher deklarieren die Variable als extern in MyClass.h-Datei (die die untergeordneten Klassen enthalten):

extern NSArray *documentsDir; 

@interface MyClass : NSObject 
... 
@end 

Wenn Sie die Variable als extern vorge deklarieren, müssen Sie entfernen Sie das static Schlüsselwort aus der Definition der Kompilierung zu vermeiden Fehler. Dies ist notwendig, damit die Variable mehrere Übersetzungseinheiten umfassen kann. (Ah, die Freuden des C ...)

Hinweis: In Objective-C-Code, der bessere Weg etwas wie extern zu erklären ist OBJC_EXPORT zu verwenden (a #define in <objc/objc-api.h> erklärt), die gesetzt basiert ob du C++ benutzt oder nicht. Ersetzen Sie einfach extern durch OBJC_EXPORT und Sie sind fertig.


Edit: ich zufällig auf einem related SO question.

+0

Danke, aber wenn ich mein Projekt bauen, erhalte ich die folgende Warnung: 'searchPath' definiert ist, aber nicht verwendet. Die Warnung wird in allen Dateien angezeigt, außer in den Fällen, in denen sie einfach verwendet wird. Die Datei mit einer definierten Konstante ist in den vorkompilierten Headern enthalten. Gibt es eine Möglichkeit, diese Warnung los zu werden? Danke. –

+0

Das liegt daran, dass das Symbol in mehrere Dateien importiert wird. Da die fragliche Datei in vielen anderen Dateien enthalten ist, verwenden Sie meine Anleitung zu Unterklassen - deklarieren Sie die Variable als extern in der Kopfzeile und wählen Sie dann nur eine Stelle (in einer .m-Datei) aus, um sie als statisch zu deklarieren (möglicherweise anders) Ort, um es zu initialisieren. Wenn Sie dies bereits tun und der Fehler weiterhin auftritt, setzen Sie die Variablendeklaration mit __attribute __ ((unused)) voran, um den Compiler anzuweisen, Warnungen über nicht verwendete Symbole zu unterdrücken. (Ich persönlich verwende #define UNUSED __attribute __ ((unused)) als Abkürzung dafür.) –

+0

Beachten Sie, dass initialize mehrmals aufgerufen wird, wenn MyClass unterklassifiziert ist. Wenn Sie also initialize verwenden möchten, müssen Sie Folgendes verwenden: if (self == [Klasse MyClass]) { searchPath == ...; } –

12

Die einfachste Lösung ist nur Pfade zu ändern, um eine statische Variable zu sein und evalutate es nur einmal, wie folgt aus:

+(NSString *) getDocumentsDir { 
    static NSString *documentsDir = nil; 
    if (!documentsDir) { 
     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES); 
     documentsDir = [paths objectAtIndex: 0]; 
    } 
    return documentsDir; 
} 

Die „statische“ weist den Compiler an, die documentsDir effektiv eine globale Variable, obwohl nur zugänglich innerhalb der Funktion. Daher wird es auf null initialisiert, und der erste Aufruf von getDocumentsDir wird es evaluieren, und dann geben weitere Aufrufe den vorvalidierten Wert zurück.

+0

+1 Guter Anruf, ich habe vergessen, dass Sie eine statische Variable innerhalb einer Funktion oder Methode deklarieren können! Dies ist in der Tat der einfachste Weg, aber bedenken Sie, dass keine andere Kompilierungseinheit (Unterklasse oder anderes) in der Lage sein wird, auf das Symbol zu verweisen. Wenn Sie nur die Konstante in einer Datei benötigen, versuchen Sie diesen Ansatz. –

+1

Genau das, was ich brauchte, um ein sich ständig änderndes Array von Zahlen zur Verfügung zu stellen, ohne es immer wieder neu zu erstellen. Wenn Sie DocumentsDir in Ihren Klassenmethoden verwenden möchten, platzieren Sie "static NSString * documentsDir = nil;" außerhalb der Klassenmethode. –

1

Kleine Optimierung in Bezug auf Peter N Lewis Code:

-(NSString *) documentsDir { 
    static NSString *documentsDir = nil; 
    return documentsDir ?: (documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]); 
} 
+0

Ist das eigentlich eine Optimierung? Ich meine, ist das irgendwie schneller? (Sieht aus, als wäre es genau dasselbe für mich.) Bist du sicher, dass es nicht nur weniger Charaktere sind? Wenn letzteres, stimme ich mit dieser Methode auf Lesbarkeitsprinzip nicht überein. Es ist unnötig verwirrend, IMO. – livingtech

+0

Nicht schneller, aber sicherer: Ich habe "objectAtIndex: 0" mit "lastObject" geändert. –

Verwandte Themen