This answer is explained by why "initializer element is not constant" .
In the following example:
SEL theSelector; // Global variable void func(void) { theSelector = @selector(constantSelector:test:); }
Compiles something like this for the i386 architecture:
.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
This part defines two local (in terms of assembly code) “variables” (actually labels), L_OBJC_METH_VAR_NAME_4 and L_OBJC_SELECTOR_REFERENCES_5 . The text .objc_meth_var_names and .objc_message_refs , immediately before the labels of 'variable', tells the assembler which section of the object file contains the “material that follows”. Sections make sense to the linker. L_OBJC_SELECTOR_REFERENCES_5 initially set to L_OBJC_METH_VAR_NAME_4 .
At the time of loading the application, before the program starts execution, the linker will do something like this:
- Iterate over each entry in the
.objc_message_refs section. - Each entry is initially set to a pointer to a string of
0 terminated C - In our example, the pointer was initially set to the address
L_OBJC_METH_VAR_NAME_4 , which contains the ASCII C string "constantSelector:test:" . - Then it executes
sel_registerName("constantSelector:test:") and stores the return value in L_OBJC_SELECTOR_REFERENCES_5 . A linker that knows the details of a private implementation cannot call sel_registerName() literally.
Essentially, the linker does this at boot time for our example:
L_OBJC_SELECTOR_REFERENCES_5 = sel_registerName("constantSelector:test:");
This is why the "initializer element is not constant" - the initializer must be constant at compile time. The value is not actually known until the program starts execution. Even then, your struct declarations are saved in another linker section, the .data section. The linker only knows how to update the SEL values in the .objc_message_refs section, and there is no way to "copy" this calculated SEL runtime from .objc_message_refs to some arbitrary place in .data .
Source Code C ...
theSelector = @selector(constantSelector:test:);
... becomes:
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;
Since the linker does all its work before the program L_OBJC_SELECTOR_REFERENCES_5 , L_OBJC_SELECTOR_REFERENCES_5 contains the same value that you would get if you called sel_registerName("constantSelector:test:") :
theSelector = sel_registerName("constantSelector:test:");
The difference is that it is a function call, and the function needs to do the actual work of finding the selector if it is already registered, or go through the process of allocating a new SEL value to register the selector. This is significantly slower than just loading a constant value. Although it is "slower", it allows you to pass an arbitrary C string. This can be useful if:
- The selector is not known at compile time.
- The selector is unknown until
sel_registerName() called. - You need to dynamically change the selector at runtime.
All selectors must pass through sel_registerName() , which registers each SEL exactly once. This has the advantage that for any given selector there is exactly one value, everywhere. Although the private detail of the SEL implementation is "usually" only a char * pointer for a copy of the text of the C selectors.
Now you know. And knowledge is half the battle!