objc_msgSend_vtable
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 theobjc_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 eachmessage_ref
, when itstrampoline
pointer is invoked for the first time,objc_msgSend_fixup
gets called receiving theobj
to which the message’s got to be sent and themessage_ref
structure from which it was called. So, whatobjc_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 thetrampoline
field of the ref by a pointer to another function that doesn’t fix the message’s selector. This function is calledobjc_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 callsobjc_msgSend_fixedup
and this just callsobjc_msgSend
. After that, if a message ref’strampoline
is called again, its selector is already fixed, andobjc_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 […]