這個答案是爲什麼"initializer element is not constant"
。
鑑於下面的例子:
SEL theSelector; // Global variable
void func(void) {
theSelector = @selector(constantSelector:test:);
}
編譯爲像這樣的i386
架構:
.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
這部分定義了兩個本地(在彙編代碼方面)「的變量」(實際上標籤),L_OBJC_METH_VAR_NAME_4
和L_OBJC_SELECTOR_REFERENCES_5
。文本.objc_meth_var_names
和.objc_message_refs
,就在'變量'標籤之前,告訴彙編器將對象文件的哪一部分放在「後面的內容」中。這些部分對鏈接器有意義。 L_OBJC_SELECTOR_REFERENCES_5
最初設置爲L_OBJC_METH_VAR_NAME_4
的地址。
在執行加載時,程序開始執行之前,所述接頭做了大約是這樣的:
- 迭代所
.objc_message_refs
部中的每個條目。
- 每個條目最初設置爲指向
0
終止的C
字符串的指針。
- 在我們的例子中,指針被初始設置爲 地址的
L_OBJC_METH_VAR_NAME_4
,其 包含ASCII
C
串 "constantSelector:test:"
。
- 然後執行
sel_registerName("constantSelector:test:")
並將返回值存儲在 L_OBJC_SELECTOR_REFERENCES_5
。鏈接器, 知道私有實現細節, 可能不會字面上調用。
本質上的連接器在加載時間爲我們的例子中執行此:
L_OBJC_SELECTOR_REFERENCES_5 = sel_registerName("constantSelector:test:");
這就是爲什麼"initializer element is not constant"
- 初始化元素必須是在編譯時間常數。在程序開始執行之前,該值實際上並不知道。即使這樣,您的struct
聲明也存儲在不同的鏈接器部分,即.data
部分。鏈接器只知道如何更新.objc_message_refs
部分中的SEL
值,並且沒有辦法將運行時計算的SEL
值從.objc_message_refs
「複製」到.data
中的某個任意位置。
的C
源代碼
theSelector = @selector(constantSelector:test:);
...變爲:
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;
由於鏈接做節目之前,其所有工作都在執行,L_OBJC_SELECTOR_REFERENCES_5
包含你的值完全相同將得到,如果你打電話sel_registerName("constantSelector:test:")
:
theSelector = sel_registerName("constantSelector:test:");
區別在於這是一個函數調用,並且該函數需要執行查找選擇器(如果它已被註冊)的實際工作,或者執行分配新的SEL
值以註冊選擇器的過程。這是相當慢,只是加載一個常數值。雖然這是'較慢',但它確實允許您傳遞任意的C
字符串。在以下情況下,這可能很有用:
- 選擇器在編譯時不知道。
- 只有在調用之前,纔會知道選擇器。
- 您需要在運行時動態改變選擇器。
所有選擇器都需要通過,這個選項只需要註冊一次SEL
。這對於任何給定的選擇器都具有恰到好處的一個值的優點。雖然實現私人細節,SEL
是「通常」,只是一個char *
指向選擇器C
字符串文本副本的指針。
現在你知道了。知道是戰鬥的一半!
我不知道爲什麼'@ selector'不是常量,但是如果你可以把初始化放到一個函數中,你就不必擔心這個。 – Amok 2009-09-11 22:10:15
我不想編寫像someMenuRects [0] .action = @doSomething這樣的代碼,因爲那麼我可能只是在運行時做同樣的事情,即如果(CGRectContainsPoint(someMenuRects [0],pt)){[self doSomething]} – 2009-09-11 22:35:32
選擇器不是常量,因爲該值在運行時非常早才確定。因此,如果需要,可以在該處粘貼一個字符串並在運行時進行查找。 – bbum 2009-09-12 06:22:25