Frage

Ich will eine Reihe von Rechtecken assoziieren mit Aktionen entsprechen, also versuchte ich,

zu tun
struct menuActions {
    CGRect rect;
    SEL action;
};

struct menuActions someMenuRects[] = {
    { { { 0, 0 }, {320, 60 } }, @selector(doSomething) },
    { { { 0, 60}, {320, 50 } }, @selector(doSomethingElse) },
};

, aber ich erhalte die Fehlermeldung „initializer Element ist nicht konstant“. Gibt es einen Grund, dass das, was ich versuche nicht, in der Regel erlaubt zu tun ist, oder überhaupt nicht globalen Bereich erlaubt, oder muss ich eine Art von kleineren Zeichensetzung Fehler haben?

War es hilfreich?

Lösung

Diese Antwort ist, warum "initializer element is not constant".

Da das folgende Beispiel:

SEL theSelector; // Global variable

void func(void) {
  theSelector = @selector(constantSelector:test:);
}

Kompiliert zu so etwas wie dies für die i386 Architektur:

  .objc_meth_var_names
L_OBJC_METH_VAR_NAME_4:
  .ascii "constantSelector:test:\0"

  .objc_message_refs
  .align 2
L_OBJC_SELECTOR_REFERENCES_5:
  .long   L_OBJC_METH_VAR_NAME_4

Dieser Teil definiert zwei lokal (in Bezug auf dem Assembler-Code) 'Variablen' (eigentlich Etikett), L_OBJC_METH_VAR_NAME_4 und L_OBJC_SELECTOR_REFERENCES_5. Der Text .objc_meth_var_names und .objc_message_refs, kurz vor den ‚Variable‘ Etikett, erzählen die Assembler, den Abschnitt der Objektdatei „das Zeug, das folgt“ zu setzen. Die Abschnitte sind sinnvoll, um den Linker. L_OBJC_SELECTOR_REFERENCES_5 wird zunächst an die Adresse von L_OBJC_METH_VAR_NAME_4 eingestellt.

Bei der Ausführung der Ladezeit, bevor das Programm die Ausführung beginnt, hat der Linker etwas ungefähr wie folgt aus:

  • Iteriert über jeden Eintrag in der .objc_message_refs Abschnitt.
  • Jeder Eintrag anfänglich auf einen Zeiger auf einen 0 beendet C Zeichenfolge festgelegt.
  • In unserem Beispiel wird der Zeiger zunächst auf die eingestellte Adresse des L_OBJC_METH_VAR_NAME_4, die enthält die ASCII C Zeichenfolge "constantSelector:test:".
  • Es führt dann sel_registerName("constantSelector:test:") und speichert den zurückgegebenen Wert an L_OBJC_SELECTOR_REFERENCES_5. Der Linker, was weiß private Implementierungsdetails, kann nicht wörtlich nennen sel_registerName().

Im Wesentlichen führt der Linker dieses zur Ladezeit für unser Beispiel:

L_OBJC_SELECTOR_REFERENCES_5 = sel_registerName("constantSelector:test:");

Aus diesem Grunde ist das "initializer element is not constant"- das initializer Element bei der Kompilierung konstant sein muss. Der Wert wird nicht wirklich bekannt, bis das Programm die Ausführung beginnt. Selbst dann sind Ihre struct Erklärungen in einem anderen Linkerabschnitt gespeichert, der .data Abschnitt. Der Linker nur weiß, wie SEL Werte im .objc_message_refs Abschnitt zu aktualisieren, und es gibt keine Möglichkeit, ‚Kopie‘ diese Laufzeit berechnet SEL Wert von .objc_message_refs an eine beliebige Stelle in .data.

Die C Quellcode ...

theSelector = @selector(constantSelector:test:);

... wird:

  movl    L_OBJC_SELECTOR_REFERENCES_5, %edx // The SEL value the linker placed there.
  movl    L_theSelector$non_lazy_ptr, %eax   // The address of theSelector.
  movl    %edx, (%eax)                       // theSelector = L_OBJC_SELECTOR_REFERENCES_5;

Da der Linker tut all seine Arbeit, bevor das Programm ausgeführt wird, L_OBJC_SELECTOR_REFERENCES_5 enthält den exakt gleichen Wert, den Sie erhalten würden, wenn Sie sel_registerName("constantSelector:test:") nennen sind:

theSelector = sel_registerName("constantSelector:test:");

Der Unterschied ist dies ein Funktionsaufruf ist, und die Funktion muss die eigentliche Arbeit des Findens der Wähler tun, wenn seine bereits registriert, oder der Prozess durchlaufen einen neuen SEL Wert der Verteilung der Wähler zu registrieren. Das ist deutlich langsamer, die nur einen konstanten Wert zu laden. Obwohl diese ‚langsamer‘ ist, tut es erlauben Sie einen beliebigen C Zeichenfolge zu übergeben. Dies kann nützlich sein, wenn:

  • Der Wähler wird bei der Kompilierung nicht bekannt.
  • Der Wähler ist bekannt, erst kurz vor sel_registerName() genannt wird.
  • Sie müssen die Wähler zur Laufzeit dynamisch verändern.

müssen alle Selektoren durch sel_registerName() passieren, die jede SEL genau einmal registriert. Dies hat den Vorteil, genau einen Wert, überall, für jeden gegebenen Wähler. Obwohl eine Implementierung privaten Detail ist SEL „in der Regel“ nur ein char * Zeiger auf eine Kopie der Selektoren C Zeichenfolge Text.

Jetzt wissen Sie. Und zu wissen, ist das halbe Leben!

Andere Tipps

Wie wäre:

struct menuActions {
   CGRect rect;
   const char *action;
};

struct menuActions someMenuRects[] = {
   { { { 0, 0 }, {320, 60 } }, "doSomething" },
   { { { 0, 60}, {320, 50 } }, "doSomethingElse" },
};

Zur Laufzeit registrieren die Wähler:

int numberOfActions = 2;
for (int i=0; i < numberOfActions; i++)
   NSLog (@"%s", sel_registerName(someMenuRects[i].action));

Ausgabe:

[Session started at 2009-09-11 16:16:12 -0700.]
2009-09-11 16:16:14.527 TestApp[12800:207] @selector(doSomething)
2009-09-11 16:16:14.531 TestApp[12800:207] @selector(doSomethingElse)

Mehr über sel_registerName() an der Objective-C 2.0 Runtime Referenz .

Sieht aus wie Sie NSCell sind neu zu erfinden hier. Wenn Sie ein Menü implementieren möchten, warum nicht UI-Klassen verwenden bestehende?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top