Iphone 我可以在结构中放置objective-C选择器吗?
我想将一组矩形与相应的动作关联起来,所以我尝试这样做Iphone 我可以在结构中放置objective-C选择器吗?,iphone,objective-c,selector,Iphone,Objective C,Selector,我想将一组矩形与相应的动作关联起来,所以我尝试这样做 struct menuActions { CGRect rect; SEL action; }; struct menuActions someMenuRects[] = { { { { 0, 0 }, {320, 60 } }, @selector(doSomething) }, { { { 0, 60}, {320, 50 } }, @selector(doSomethingElse) }, }; 但是
struct menuActions {
CGRect rect;
SEL action;
};
struct menuActions someMenuRects[] = {
{ { { 0, 0 }, {320, 60 } }, @selector(doSomething) },
{ { { 0, 60}, {320, 50 } }, @selector(doSomethingElse) },
};
但是我得到了错误“初始化器元素不是常量”。是否有某种原因导致我尝试做的事情通常是不允许的,或者在全局范围内是不允许的,或者我有一些小的标点错误?如何:
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));
有关
选择注册表名()
的详细信息,请参见。这是为什么的“初始值设定项元素不是常量”
给出以下示例:
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_选择器_REFERENCES_5
最初设置为L_OBJC_METH_VAR_NAME_4
的地址
在执行加载时,在程序开始执行之前,链接器大致执行如下操作:
- 迭代
节.objc\u消息\u refs
- 每个条目最初设置为指向
终止的0
字符串的指针C
- 在我们的示例中,指针最初设置为
的地址,其中 包含L_OBJC_METH_VAR_NAME_4
ASCII
字符串 constantSelector:test:“”C
- 然后它执行
并将返回值存储在sel\u registerName(“constantSelector:test:”)
。链接器, 它知道私人实施的细节, 不能直接调用L\u OBJC\u选择器\u引用\u 5
sel\u registerName()
L_OBJC_SELECTOR_REFERENCES_5 = sel_registerName("constantSelector:test:");
这就是为什么“initializer元素不是常量”
-initializer元素在编译时必须是常量。在程序开始执行之前,该值实际上是未知的。即使如此,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:”)时得到的值完全相同的值。
:
区别在于这是一个函数调用,如果选择器已经注册,则函数需要执行查找选择器的实际工作,或者执行分配新的SEL
值以注册选择器的过程。这比仅加载常量值要慢得多。虽然这“较慢”,但它确实允许您传递任意C
字符串。在以下情况下,这可能很有用:
- 选择器在编译时未知
- 只有在调用
之前,才知道选择器sel\u registerName()
- 您需要在运行时动态更改选择器
sel\u registerName()
,它将每个sel
准确地注册一次。这样做的优点是,对于任何给定的选择器,在任何地方都只有一个值。尽管是实现私有细节,SEL
通常只是一个指向选择器C
字符串文本副本的char*
指针
现在你知道了。知道是成功的一半 看来你正在重新发明NSCell。如果你想实现一个菜单,为什么不使用现有的UI类呢?我不知道为什么
@selector
不是常量,但是如果你能将初始化放在一个函数中,你就不必担心这个问题。我不想编写像某些菜单[0]那样的代码。action=@doSomething,因为我也可以在运行时做同样的事情,也就是说,如果(CGRectContainsPoint(someMenuRects[0],pt)){[self doSomething]}选择器不是常量,因为直到运行时很早时才真正确定值。因此,如果您愿意,您可以在其中插入一个字符串并在运行时进行查找。因此,现在我使用的是一个(char*)的静态表,只要在选择选择器时通过sel_registerName调用它,因为它是UI代码,额外的调用并不重要。是的,我知道sel_registerName,但原则上,在编译时真正确定这一步骤时,在运行时执行这一步骤似乎是错误的。也许我的问题的答案真的只是“不,你不能”,我应该问另一个问题,比如“在Objective-C中构建函数调度表的最佳方法是什么”……我并不是想假设你对此一无所知。这是一个有趣的问题,但我不知道正确的答案。对不起,如果我听起来很生气——我在这个平台上太无知了!谢谢你提供的细节。我假设sel_registerName发生在编译/链接时,而不是加载时,对于常量选择器。。。所以我想,如果你称之为>1次,sel_registerName实际上只是“较慢”…哇。。。我不理解这一半,但听起来你知道你在说什么)+1“如果选择器已经注册,函数需要做查找选择器的实际工作”:没错。例如,如果应用程序源文件使用了@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;
theSelector = sel_registerName("constantSelector:test:");