[wx-dev] Obj-C "class unifiying"?

David Elliott dfe at cox.net
Sun May 27 10:50:34 PDT 2007


Hi Robert,

On May 27, 2007, at 4:35 AM, Robert Roebling wrote:

>
> What is "class unification"?
>
>   Robert
>
> Modified Files:
> 	NSButton.mm NSControl.mm NSView.mm app.mm control.mm
> 	radiobox.mm toplevel.mm window.mm
> Log Message:
> Allow many Objective-C classes to be uniquified at runtime.

Not unification, uniquification.  That is, to make the class names  
unique at runtime.

Imagine a situation where wxWidgets is built once as a static  
library.  Now imagine that you are developing something a bundle  
(plugin) for a Cocoa app.  Let's take the stock Mail.app as an  
example.  Now, suppose you need to build two different bundles to do  
two different things.  One bundle implements some new message  
transfer protocol (say like the HTTPMail plugin).  Another bundle  
implements some user interface enhancement (say like the widescreen  
mail plugin).

One can keep the two wxWidgets copies contained within the bundle by  
telling the linker not to export any symbols from the library when  
building the Mach-O bundles.  This will ensure that each plugin has  
its own self-contained copy of wxWidgets.  Unfortunately, when  
Objective-C classes are defined there is a big gotcha with that.  You  
see, the Objective-C runtime keeps track of all classes by name.  If  
it encounters a class with the same name as one already loaded it  
will simply ignore it.

Thus what happens is the first bundle has its WXNSView class loaded.   
Objective-C reads through the Mach-O bundle (the __OBJC, __class  
section) and maps the class name (using the objc_class name data  
member) to the class structure (the objc_class itself).  From then  
on, the name alone can be used to get a pointer to the objc_class  
structure which is what is needed to send class messages like alloc.

Sometime later the program creates a WXNSView (by doing the normal  
[WXNSView alloc].  The actual assembler for this is to create a class  
reference in the __OBJC, __cls_refs section then to use that  
reference as the message receiver (the first implicit self argument  
of any Objective-C message send) and @selector(alloc) as the selector  
(the second implicit cmd argument of any Objective-C message send).   
Upon compilation, the class reference is a plain C string literally  
"WXNSView".  Upon bundle load the Objective-C runtime looks through  
the bundle's __OBJC, __cls_refs section and replaces the strings with  
pointers to objc_class which works because const char* is the same  
size (4 or 8 bytes) as struct objc_class*.  It does this using its  
internal class hash which maps class names to class structures.

Now the second bundle is loaded.  Objective-C reads through the  
__OBJC, __class section but finds that it already has these classes.   
It thus ignores them entirely.  Now later on when the second bundle  
creates an NSView the same process occurs as before except the  
returned objc_class pointer is a pointer to the originally loaded class.

What the uniquifying code does is to uniquify the name at runtime.   
Unfortunately, this is somewhat of a misrepresentation of all that it  
does because the primary thing is to not use __OBJC, __cls_refs when  
sending a class message but to instead use a pointer to the compiler- 
generated objc_class structure.  Thus why you now see  
[WX_GET_OBJC_CLASS(WXNSView) alloc].  What happens here is that this  
causes the compiler to generate code to get its own generated  
objc_class directly.  This bypasses the class hash entirely and  
corrects the problem.

You may notice upon looking at the code that it does just a bit more  
than that.  You see, when the Objective-C runtime does not load a  
class (because it has already loaded one with the same name) it does  
not fix up the super_class or isa.isa or isa.super_class pointers.   
Those are needed for messaging to work.  The wx code can't be certain  
whether the runtime did or did not fix up the class structure so it  
simply always does it upon first uses (hence the function static  
variable).

Do note that this hackery is more compiler-specific than anything.   
The only alternative to this is to create the class structures  
manually and fill in the method lists with pointers to C functions.   
That is a possible way to do it but then consider the case of [super  
doSomething].  The super in that statement is actually a language  
keyword not a function parameter (self is an implicit parameter like  
this is in C++).  The super keyword can only ever appear in a  
method's implementation and the compiler implements it by using the  
super_class data member of the compiler generated objc_class for the  
class that the method is being implemented in.  So within  
@implementation WXNSView, the super keyword means to use the second  
pointer of L_OBJC_CLASS_WXNSView.  That's why the fixup of  
super_class and so on occurs not on a copy of the objc_class  
structure but directly on the compiler-generated structure.

This superclass pointer is then used with the objc_msgSendSuper  
function instead of the objc_msgSend function.  Like objc_msgSend, it  
takes a self (the instance) and a cmd (the selector) along with the  
varargs parameters.  The difference is that instead of using self- 
 >isa to look up the instance methods it instead takes the objc_class  
pointer as a parameter.

There is no Objective-C syntax besides super to cause the compiler to  
generate calls to objc_msgSendSuper so if we wanted to implement  
Objective-C methods as plain C methods we'd have to use  
objc_msgSendSuper.  That is why I chose to let the compiler do most  
of the work and then fix up the structures rather than going all out  
and generating the objc_class structures purely at runtime.

So, there you have it.  That's what the code does.  Now, can I ask  
you a question?  Why do you ask?

-Dave





More information about the wx-dev mailing list