Questa risposta è il motivo per cui "initializer element is not constant"
.
Dato il seguente esempio:
SEL theSelector; // Global variable
void func(void) {
theSelector = @selector(constantSelector:test:);
}
compila a qualcosa di simile per la i386
architettura:
.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
questo componente definisce due 'variabili' locali (in termini di codice assembly) (in realtà etichette), L_OBJC_METH_VAR_NAME_4
e L_OBJC_SELECTOR_REFERENCES_5
. Il testo .objc_meth_var_names
e .objc_message_refs
, appena prima delle etichette 'variabile', dice all'assemblatore quale sezione del file oggetto inserisce "le cose che seguono". Le sezioni sono significative per il linker. L_OBJC_SELECTOR_REFERENCES_5
viene inizialmente impostato all'indirizzo L_OBJC_METH_VAR_NAME_4
.
Al momento del caricamento di esecuzione, prima che il programma inizia l'esecuzione, il linker fa qualcosa circa come questo:
- iterazioni sulle ogni voce nella sezione
.objc_message_refs
.
- Ogni voce è inizialmente impostata su un puntatore a una stringa
0
terminata C
.
- Nel nostro esempio, il puntatore viene inizialmente impostato l'indirizzo di
L_OBJC_METH_VAR_NAME_4
, che contiene la stringa "constantSelector:test:"
ASCII
C
.
- Quindi esegue
sel_registerName("constantSelector:test:")
e memorizza il valore restituito a L_OBJC_SELECTOR_REFERENCES_5
. Il linker che conosce i dettagli di implementazione privata, non può chiamare letteralmente .
Essenzialmente il linker esegue questo al tempo di caricamento per il nostro esempio:
L_OBJC_SELECTOR_REFERENCES_5 = sel_registerName("constantSelector:test:");
Ecco perché la "initializer element is not constant"
- elemento dell'inizializzatore deve essere costante in fase di compilazione. Il valore non è noto fino a quando il programma non inizia l'esecuzione. Anche in questo caso, le tue dichiarazioni struct
vengono memorizzate in una sezione diversa del linker, la sezione .data
. Il linker sa solo come aggiornare i valori SEL
nella sezione .objc_message_refs
e non è possibile "copiare" il valore di runtime calcolato SEL
da .objc_message_refs
in una posizione arbitraria in .data
.
Il codice sorgente di C
...
theSelector = @selector(constantSelector:test:);
... diventa:
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;
Dal momento che il linker fa tutto il suo lavoro prima che il programma è in esecuzione, L_OBJC_SELECTOR_REFERENCES_5
contiene lo stesso valore esatto otterrebbe se si dovesse chiamare sel_registerName("constantSelector:test:")
:
theSelector = sel_registerName("constantSelector:test:");
La differenza è che si tratta di una chiamata di funzione e che la funzione deve eseguire il lavoro effettivo per trovare il selettore se è già stato registrato oppure passare attraverso il processo di allocazione di un nuovo valore SEL
per registrare il selettore. Questo è molto più lento che basta caricare un valore costante. Sebbene questo sia 'più lento', ti consente di passare una stringa arbitraria C
. Questo può essere utile se:
- Il selettore non è noto al momento della compilazione.
- Il selettore non è noto fino a poco prima che venga chiamato .
- È necessario variare il selettore in modo dinamico in fase di esecuzione.
Tutti i selettori devono passare attraverso , che registra ogni SEL
esattamente una volta. Questo ha il vantaggio di avere esattamente un valore, ovunque, per ogni dato selettore. Sebbene sia un dettaglio privato dell'implementazione, SEL
è "di solito" solo un puntatore char *
a una copia dei selettori C
testo stringa.
Ora lo sai. E sapere è metà della battaglia!
Non so perché '@ selector' non è costante, ma se si può mettere l'inizializzazione in una funzione non ci si deve preoccupare di ciò. – Amok
Non volevo scrivere codice come alcuniMenuRects [0] .action = @doSomething, perché allora potrei anche fare la stessa cosa in fase di runtime, cioè se (CGRectContainsPoint (someMenuRects [0], pt)) {[self doSomething]} –
Un selettore non è costante perché il valore non è realmente determinato fino a molto all'inizio del runtime. Pertanto, puoi inserire una stringa e fare una ricerca in fase di esecuzione, se lo desideri. – bbum