#define vs const in Objective-C

81

Ich bin neu in Objective-C und habe einige Fragen dazu const und zur Vorverarbeitungsrichtlinie #define.

Zunächst stellte ich fest, dass es nicht möglich ist, den Typ der Konstante mit zu definieren #define . Warum ist das so?

Zweitens gibt es irgendwelche Vorteile, einen von ihnen gegenüber dem anderen zu verwenden?

Welcher Weg ist schließlich effizienter und / oder sicherer?

ObjectiveCoder
quelle

Antworten:

107

Zuerst stellte ich fest, dass es nicht möglich ist, den Typ der Konstante mit #define zu definieren. Warum ist das so?

Warum ist was? Es ist nicht wahr:

#define MY_INT_CONSTANT ((int) 12345)

Zweitens gibt es irgendwelche Vorteile, einen von ihnen gegenüber dem anderen zu verwenden?

Ja. #definedefiniert ein Makro, das bereits vor Beginn der Kompilierung ersetzt wird. constÄndert lediglich eine Variable so, dass der Compiler einen Fehler anzeigt, wenn Sie versuchen, ihn zu ändern. Es gibt Kontexte, in denen Sie a verwenden können, #defineaber Sie können a nicht verwenden const(obwohl ich Schwierigkeiten habe, einen mit dem neuesten Clang zu finden). Theoretisch constnimmt a Platz in der ausführbaren Datei ein und erfordert einen Verweis auf den Speicher. In der Praxis ist dies jedoch unbedeutend und kann vom Compiler optimiert werden.

consts sind viel compiler- und debuggerfreundlicher als #define s. In den meisten Fällen ist dies der übergeordnete Punkt, den Sie berücksichtigen sollten, wenn Sie eine Entscheidung treffen, welchen Sie verwenden möchten.

Ich dachte nur an einen Kontext, den Sie verwenden können, #defineaber nicht const. Wenn Sie eine Konstante haben, die Sie in vielen .cDateien verwenden möchten, #definestecken Sie sie einfach in einen Header. Mit a müssen constSie eine Definition in einer C-Datei haben und

// in a C file
const int MY_INT_CONST = 12345;

// in a header
extern const int MY_INT_CONST;

in einem Header. MY_INT_CONSTkann nicht als Größe eines statischen oder globalen Bereichsarrays in einer C-Datei verwendet werden, außer der, in der es definiert ist.

Für ganzzahlige Konstanten können Sie jedoch eine verwenden enum. Genau das macht Apple fast immer. Dies hat alle Vorteile von #defines und consts, funktioniert jedoch nur für ganzzahlige Konstanten.

// In a header
enum
{
    MY_INT_CONST = 12345,
};

Welcher Weg ist schließlich effizienter und / oder sicherer?

#defineist theoretisch effizienter, obwohl moderne Compiler, wie gesagt, wahrscheinlich sicherstellen, dass es kaum Unterschiede gibt. #defineist insofern sicherer, als es immer ein Compilerfehler ist, zu versuchen, ihm zuzuweisen

#define FOO 5

// ....

FOO = 6;   // Always a syntax error

consts kann dazu verleitet werden, zugewiesen zu werden, obwohl der Compiler möglicherweise Warnungen ausgibt:

const int FOO = 5;

// ...

(int) FOO = 6;     // Can make this compile

Abhängig von der Plattform kann die Zuweisung zur Laufzeit immer noch fehlschlagen, wenn die Konstante in einem schreibgeschützten Segment platziert wird und das Verhalten gemäß dem C-Standard offiziell undefiniert ist.

Persönlich verwende ich für ganzzahlige Konstanten immer enums für Konstanten anderer Typen, es constsei denn, ich habe einen sehr guten Grund, dies nicht zu tun.

JeremyP
quelle
Ich weiß, dass es alt ist, aber eine Instanz, in der Sie keine Konstante verwenden können, die Sie definieren könnten, ist "#define MY_NSNUM_CONSTANT @ 5", was mit "NSNumber * const MY_NSNUM_CONSTANT = @ 5"
shortstuffsushi
Ich würde argumentieren, dass #define mehr Platz in der ausführbaren Datei einnimmt als const. Eine Konstante wird einmal gespeichert, aber ein #define wird jedes Mal multipliziert, wenn Sie es verwenden, da es sich nur um Textersetzung handelt. Aber da der Unterschied unbedeutend ist, bin ich unnötig pedantisch.
Ryan Ballantyne
@RyanBallantyne Ich denke, Sie sind vielleicht richtig für Dinge wie String-Konstanten, aber nicht für Integer-Konstanten, denn selbst wenn Sie die Konstante an einem Ort speichern, benötigen Sie für den Zugriff darauf ihre Adresse, die mindestens so groß wie eine ist int. Ich wäre jedoch sehr überrascht, wenn es in einem modernen Compiler überhaupt einen Unterschied machen würde.
JeremyP
16

Von einem C-Codierer:

A constist einfach eine Variable, deren Inhalt nicht geändert werden kann.

#define name valueEs handelt sich jedoch um einen Präprozessorbefehl, der alle Instanzen von namemit ersetzt value.

Wenn Sie dies #define defTest 5beispielsweise defTesttun, werden 5beim Kompilieren alle Instanzen in Ihrem Code durch ersetzt .

MegaNairda
quelle
11

Es ist wichtig, den Unterschied zwischen den Anweisungen #define und const zu verstehen, die nicht für dieselben Dinge bestimmt sind.

const

constwird verwendet, um ein Objekt aus dem angeforderten Typ zu generieren, das nach der Initialisierung konstant ist. Dies bedeutet, dass es sich um ein Objekt im Programmspeicher handelt und als schreibgeschützt verwendet werden kann. Das Objekt wird bei jedem Programmstart generiert.

#define

#definewird verwendet, um die Lesbarkeit des Codes und zukünftige Änderungen zu erleichtern. Wenn Sie eine Definition verwenden, maskieren Sie nur einen Wert hinter einem Namen. Wenn Sie mit einem Rechteck arbeiten, können Sie daher Breite und Höhe mit entsprechenden Werten definieren. Dann ist es im Code einfacher zu lesen, da anstelle von Zahlen Namen stehen.

Wenn Sie sich später dazu entschließen, den Wert für die Breite zu ändern, müssen Sie ihn nur in der Definition ändern, anstatt ein langweiliges und gefährliches Suchen / Ersetzen in Ihrer gesamten Datei. Beim Kompilieren ersetzt der Präprozessor den gesamten definierten Namen durch die Werte im Code. Daher geht mit ihnen keine Zeit verloren.

bürgerlich
quelle
7

Zusätzlich zu den Kommentaren anderer Leute ist #definees bekanntermaßen schwierig , Fehler zu verwenden , da der Vorprozessor sie vor dem Compiler abruft.

Ed Heal
quelle
3

Da Pre-Prozessor-Direktiven verpönt sind, empfehle ich die Verwendung von a const. Sie können keinen Typ mit einem Vorprozessor angeben, da eine Vorprozessoranweisung vor dem Kompilieren aufgelöst wird. Nun, Sie können, aber so etwas wie:

#define DEFINE_INT(name,value) const int name = value;

und benutze es als

DEFINE_INT(x,42) 

was vom Compiler als gesehen werden würde

const int x = 42;

Zuerst stellte ich fest, dass es nicht möglich ist, den Typ der Konstante mit #define zu definieren. Warum ist das so?

Sie können meinen ersten Ausschnitt sehen.

Zweitens gibt es irgendwelche Vorteile, einen von ihnen gegenüber dem anderen zu verwenden?

Im Allgemeinen consthilft eine Direktive anstelle einer Vorprozessor-Direktive beim Debuggen, in diesem Fall nicht so sehr (aber immer noch).

Welcher Weg ist schließlich effizienter und / oder sicherer?

Beide sind genauso effizient. Ich würde sagen, das Makro kann möglicherweise sicherer sein, da es zur Laufzeit nicht geändert werden kann, wohingegen eine Variable dies könnte.

Luchian Grigore
quelle
Warum einfach die const ...Eingabe eingeben, anstatt ein Makro zu verwenden?
Ed Heal
1
@ EdHeal nicht. Ich habe nur gesagt, dass Sie als Antwort auf "Ich habe festgestellt, dass es nicht möglich ist, den Typ der Konstante mit #define zu definieren, warum ist das so?"
Luchian Grigore
1
> pre-processor directives are frowned upon[Zitat benötigt]
Ben Leggiero
Ich denke, Sie können einen Typ wie diesen verwenden: #define myValue ((double) 2). So wie ich es verstehe, ersetzt der Präprozessor "myValue" nur durch das, was in der define-Anweisung danach steht, einschließlich der Typinformationen.
vomako
1

Ich habe #define schon einmal verwendet, um mehr Methoden aus einer Methode zu erstellen, als ob ich so etwas hätte.

 // This method takes up to 4 numbers, we don't care what the method does with these numbers.
 void doSomeCalculationWithMultipleNumbers:(NSNumber *)num1 Number2:(NSNumber *)num2 Number3:(NSNumber *)num23 Number3:(NSNumber *)num3;

Aber ich möchte auch eine Methode haben, die nur 3 Zahlen und 2 Zahlen akzeptiert. Anstatt zwei neue Methoden zu schreiben, werde ich dieselbe mit #define verwenden.

 #define doCalculationWithFourNumbers(num1, num2, num3, num4) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), (num3), (num4))

 #define doCalculationWithThreeNumbers(num1, num2, num3) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), (num3), nil)

 #define doCalculationWithTwoNumbers(num1, num2) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), nil, nil)

Ich denke, das ist eine ziemlich coole Sache. Ich weiß, dass Sie direkt zur Methode gehen und einfach Null in die Räume setzen können, die Sie nicht wollen, aber wenn Sie eine Bibliothek erstellen, ist dies sehr nützlich. Auch so geht das

     NSLocalizedString(<#key#>, <#comment#>)
     NSLocalizedStringFromTable(<#key#>, <#tbl#>, <#comment#>)
     NSLocalizedStringFromTableInBundle(<#key#>, <#tbl#>, <#bundle#>, <#comment#>)

sind fertig.

Während ich nicht glaube, dass Sie dies mit Konstanten tun können. Konstanten haben jedoch Vorteile gegenüber #define, da Sie keinen Typ mit #define angeben können, da es sich um eine Vorprozessor-Direktive handelt, die vor dem Kompilieren aufgelöst wird. Wenn bei #define ein Fehler auftritt, ist das Debuggen schwieriger Konstanten. Beide haben Vor- und Nachteile, aber ich würde sagen, dass alles von dem Programmierer abhängt, für den Sie sich entschieden haben. Ich habe mit beiden eine Bibliothek geschrieben, indem ich #define verwendet habe, um das zu tun, was ich gezeigt habe, und Konstanten zum Deklarieren von konstanten Variablen, für die ich einen Typ angeben muss.

Popeye
quelle