If you’re loading code dynamically and using an unsupported API under Cocoa 64bit, you’ll be greeted with this unpleasant surprise:
9/7/09 2:34:06 PM Mail[8641] MailTest.mailbundle failed to load. The error was:
Error Domain=NSCocoaErrorDomain Code=3588 UserInfo=0x100563840 "The bundle “MailTest” couldn’t beloaded."
After many hours of slamming my head against the desk, and help from others (namely Erik Hinterbichler, I’ve figured out how to get around this error. Why would you want to do this in the first place? Well, it’s useful for creating addons to closed source products like Mail.app and Safari. The usual procedure involves dumping the headers for the app using class-dump, determining how to swizzle your code in, and finally figuring out how to load your code into the app.
With the switch to 64bit, Objective-C has a new dynamic loader that pays attention to symbol visibility. The 32bit loader didn’t, and would allow your code to bind to any symbol in the app. With the 64bit loader, only symbols that are explicitly exported are available for dynamic binding. As a result, nearly all the symbols in an app like Mail.app aren’t exported for binding. If you do try to bind, you’ll see that error message above.
So what’s the trick? Never reference any of the symbols directly! Instead you’re going to us NSClassFromString along with the Objective-C runtime methods available from the header <objc/runtime.h>
Here’s how I now set the super class for a Mail.app plugin:
+ (void) initialize {
class_setSuperclass([self class], NSClassFromString(@"MVMailBundle"));
[MattsMailBundle registerBundle];
}
Notice, this is done in the initialize class method, and no Mail.app symbols (in this case MVMailBundle) are referenced directly.
Hope that helps, happy hacking!