Думайте, что Вы читаете Arabii или иврит: читайте из верхней части страницы, справа налево к нижней части.; P хорошая вещь об этом "правиле" то, что это работает на 3 версии значения также: top right-and-left bottom
На самом деле, я также думал, что было трудно помнить, но как только я обнаружил, что это было направление часов, это было легче.
Этот ответ объясняет, почему «элемент инициализатора не является постоянным»
.
В следующем примере:
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
.
Во время загрузки выполнения, section.
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:");
Вот почему «элемент инициализатора не является постоянным»
- элемент инициализатора должен быть постоянным во время компиляции. Фактически значение неизвестно, пока программа не начнет выполняться. Даже в этом случае ваши объявления 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
.
Теперь вы знаете. . А знание - это половина дела!
SEL
«обычно» просто char *
указатель на копию селекторов C
текст строки.
Теперь вы знаете. А знание - это половина дела!
SEL
«обычно» просто char *
указатель на копию селекторов C
текст строки.
Теперь вы знаете. А знание - это половина дела!
Как насчет:
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)
Подробнее о sel_registerName ()
в Objective-C 2.0 Справочник по среде выполнения .
Похоже, вы заново изобретаете NSCell. Если вы хотите реализовать меню, почему бы не использовать существующие классы пользовательского интерфейса?