Saturday, June 18, 2011


Greg Parker:

The compiler knows which selectors are optimized by the runtime. It compiles the call site differently, calling objc_msgSend_fixup via a function pointer. At runtime, objc_msgSend_fixup replaces the function pointer with one of the objc_msgSend_vtable functions, if the called selector is one of the optimized selectors.

Update (2014-06-25): objc-runtime-new.m (via Colin Wheeler):

* vtable dispatch
* Every class gets a vtable pointer. The vtable is an array of IMPs.
* The selectors represented in the vtable are the same for all classes
*   (i.e. no class has a bigger or smaller vtable).
* Each vtable index has an associated trampoline which dispatches to 
*   the IMP at that index for the receiver class's vtable (after 
*   checking for NULL). Dispatch fixup uses these trampolines instead 
*   of objc_msgSend.
* Fragility: The vtable size and list of selectors is chosen at launch 
*   time. No compiler-generated code depends on any particular vtable 
*   configuration, or even the use of vtable dispatch at all.
* Memory size: If a class's vtable is identical to its superclass's 
*   (i.e. the class overrides none of the vtable selectors), then 
*   the class points directly to its superclass's vtable. This means 
*   selectors to be included in the vtable should be chosen so they are 
*   (1) frequently called, but (2) not too frequently overridden. In 
*   particular, -dealloc is a bad choice.
* Forwarding: If a class doesn't implement some vtable selector, that 
*   selector's IMP is set to objc_msgSend in that class's vtable.
* +initialize: Each class keeps the default vtable (which always 
*   redirects to objc_msgSend) until its +initialize is completed.
*   Otherwise, the first message to a class could be a vtable dispatch, 
*   and the vtable trampoline doesn't include +initialize checking.
* Changes: Categories, addMethod, and setImplementation all force vtable 
*   reconstruction for the class and all of its subclasses, if the 
*   vtable selectors are affected.


At the program startup, the message reference trampolines are pointers to the objc_msgSend_fixup function. For each message_ref, when its trampoline pointer is invoked for the first time, objc_msgSend_fixup gets called receiving the obj to which the message’s got to be sent and the message_ref structure from which it was called. So, what objc_msgSend_fixup must do is get the selector for the message to be called. Since, this has to be done only once for each message reference, objc_msgSend_fixup must also replace the trampoline field of the ref by a pointer to another function that doesn’t fix the message’s selector. This function is called objc_msgSend_fixedup (the selector has been fixed up). Now that the message selector has been set and this doesn’t have to be done again, objc_msgSend_fixup just calls objc_msgSend_fixedup and this just calls objc_msgSend. After that, if a message ref’s trampoline is called again, its selector is already fixed, and objc_msgSend_fixedup is the one that gets called.

2 Comments RSS · Twitter

One thing you can't avoid noticing when looking at the broad picture is that Objective-C is very well suited to provide OS-level APIs due to everything being dynamically bound. Compare to C++ vtable's fragile design which is faster but where adding a virtual function in a class breaks all subclasses compiled against the earlier version, and you can understand why very few operating systems offer C++ APIs (that aren't simple wrappers).

[…] see performance regressions, since the frequently used reference counting selectors are no longer optimized in this […]

Leave a Comment