这个答案是为什么"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