I was wanting to associate a set of rectangles with corresponding actions, so I tried to do
struct menuActions {
CGRect rect;
SEL action;
};
struct menuActions someMenuRects[] = {
{ { { 0, 0 }, {320, 60 } }, @selector(doSomething) },
{ { { 0, 60}, {320, 50 } }, @selector(doSomethingElse) },
};
but I get the error “initializer element is not constant”. Is there some reason that what I’m trying to do isn’t allowed in general, or isn’t allowed at global scope, or do I have some kind of minor punctuation mistake?
This answer is to why
"initializer element is not constant".Given the following example:
Compiles to something like this for the
i386architecture:This part defines two local (in terms of assembly code) ‘variables’ (actually labels),
L_OBJC_METH_VAR_NAME_4andL_OBJC_SELECTOR_REFERENCES_5. The text.objc_meth_var_namesand.objc_message_refs, just before the ‘variable’ labels, tells the assembler which section of the object file to put “the stuff that follows”. The sections are meaningful to the linker.L_OBJC_SELECTOR_REFERENCES_5is initially set to the address ofL_OBJC_METH_VAR_NAME_4.At execution load time, before the program begins executing, the linker does something approximately like this:
.objc_message_refssection.
0terminatedCstring.address of
L_OBJC_METH_VAR_NAME_4, whichcontains the
ASCIICstring"constantSelector:test:".sel_registerName("constantSelector:test:")and stores the returned value at
L_OBJC_SELECTOR_REFERENCES_5. The linker,which knows private implementation details,
may not call
sel_registerName()literally.Essentially the linker performs this at load time for our example:
This is why the
"initializer element is not constant"– the initializer element must be constant at compile time. The value is not actually known until the program begins executing. Even then, yourstructdeclarations are stored in a different linker section, the.datasection. The linker only knows how to updateSELvalues in the.objc_message_refssection, and there is no way to ‘copy’ that run-time calculatedSELvalue from.objc_message_refsto some arbitrary location in.data.The
Csource code…… becomes:
Since the linker does all its work before the program is executing,
L_OBJC_SELECTOR_REFERENCES_5contains the exact same value you would get if you were to callsel_registerName("constantSelector:test:"):The difference is this is a function call, and the function needs to do the actual work of finding the selector if its already been registered, or go through the process of allocating a new
SELvalue to register the selector. This is considerably slower that just loading a constant value. Though this is ‘slower’, it does allow you to pass an arbitraryCstring. This can be useful if:sel_registerName()is called.All selectors need to pass through
sel_registerName(), which registers eachSELexactly once. This has the advantage of having exactly one value, everywhere, for any given selector. Though an implementation private detail,SELis “usually” just achar *pointer to a copy of the selectorsCstring text.Now you know. And knowing is half the battle!