我想要将一组矩形与相应的动作相关联,所以我试着做
struct menuActions {
CGRect rect;
SEL action;
};
struct menuActions someMenuRects[] = {
{ { { 0, 0 }, {320, 60 } }, @selector(doSomething) },
{ { { 0, 60}, {320, 50 } }, @selector(doSomethingElse) },
};
但我收到错误“初始化元素不是常数”。是否有一些理由说我想要做的事情一般不被允许,或者在全球范围内是不允许的,或者我是否有某种轻微的标点错误?
答案 0 :(得分:23)
这个答案是为什么"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
。链接器,
知道私人实施细节,
可能不会直接致电sel_registerName()
。基本上,链接器在加载时为我们的示例执行此操作:
L_OBJC_SELECTOR_REFERENCES_5 = sel_registerName("constantSelector:test:");
这就是"initializer element is not constant"
- 初始化元素在编译时必须是常量的原因。在程序开始执行之前,该值实际上是不可知的。即便如此,您的struct
声明也存储在不同的链接器部分.data
部分中。链接器只知道如何更新SEL
部分中的.objc_message_refs
值,并且无法将运行时计算的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_registerName()
之前,才知道选择器。所有选择器都需要通过sel_registerName()
,每个SEL
只注册一次。对于任何给定的选择器,这具有在任何地方具有恰好一个值的优点。虽然是实现私有详细信息,但SEL
“通常”只是指向选择器char *
字符串文本副本的C
指针。
现在你知道了。知道是成功的一半!
答案 1 :(得分:4)
怎么样:
struct menuActions {
CGRect rect;
const char *action;
};
struct menuActions someMenuRects[] = {
{ { { 0, 0 }, {320, 60 } }, "doSomething" },
{ { { 0, 60}, {320, 50 } }, "doSomethingElse" },
};
在运行时,注册选择器:
int numberOfActions = 2;
for (int i=0; i < numberOfActions; i++)
NSLog (@"%s", sel_registerName(someMenuRects[i].action));
输出:
[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)
有关Objective-C 2.0 Runtime Reference的sel_registerName()
的更多信息。
答案 2 :(得分:-1)
看起来你在这里重塑NSCell。如果要实现菜单,为什么不使用现有的UI类?