Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/iphone/36.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Iphone 我可以在结构中放置objective-C选择器吗?_Iphone_Objective C_Selector - Fatal编程技术网

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
    C
    字符串 constantSelector:test:“”
  • 然后它执行
    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:");