Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class X is implemented in both Y and Z. One of the two will be used

Tags:

objective-c

I've got an objective-c "tool" (console) program that dynamically loads objective-c bundle's at runtime. Some of the bundle files share classes from the same framework so I get the "Class is implemented in both..." message dumped out to the Console.

It doesn't prevent anything from working, but having the messages on the console is somewhat annoying. Is there a way to prevent them from being dumped out there? Is there a way that the bundle's could be changed so they don't both compile/link the same classes?

like image 456
bugfixr Avatar asked Jan 16 '12 21:01

bugfixr


2 Answers

I have been in this boat before and it's not a good boat to be in. I spent a long time investing in the best possibly way to avoid runtime class name clashes. The solution was:

  1. Create a pre-build script that parses all @interface declarations in header files, and reaps all of the class names. This script generates a new header that contains something like the following:

    #define MyClass        MyClass_Target
    #define MyOtherClass   MyOtherClass_Target
    #define MyThirdClass   MyThirdClass_Target
    

    (obviously the _Target suffix is set by a build parameter that your pre-build script is able to see and use).

    The script could be a Perl or Python script that just scans every *.h file extracting the word after @interface.

  2. Configure the build to automatically include the generated header file so you don't have to manually include the generated header in each source file.

  3. If your bundles have nibs, it adds an extra step of complexity but it can still be done. Create another script that replaces the existing “Nib Compiler”. This program basically performs the same class-renaming but for nibs (it's easy enough because the nib files are XML). I can't remember where to set it, but there's a place in Xcode that allows you to choose how Nib files are “compiled”. Your nib compiler script is basically a wrapper that renames the classes in the nibs and then invokes the real nib compiler (which you can find out how it is invoked by looking at the build log).

Does it work?

Yes, and it works surprisingly well, even in debugging situations. When you hit a breakpoint, Xcode displays the name of the class with its target suffix so you know which bundle you are in, despite the source code showing something different. “Fix & Continue” still worked as far as I can remember.

Also, because the class-renaming is done just before compile time, it doesn't affect things like source code versioning, interactions with Interface Builder, etc.

Issues

  • Dynamically generated class names won't work, e.g. Class objects returned from NSClassFromString, unless the code is made aware of the necessary suffix.

  • Such a complicated set up requires a bit of maintenance. Once I had it up and running it was working well for our needs, but every now and then it needed a tweak to keep it running smoothly.

like image 88
dreamlax Avatar answered Nov 07 '22 05:11

dreamlax


  • If they are different implementations, use a unique prefix.

  • If they're the same implementation and just exported by more than one image, reconfigure your targets so it's exported by only one.

Specifically:

Is there a way that the bundle's could be changed so they don't both compile/link the same classes?

Stuff those shared classes in a separate framework, and do not compile them as part of the bundle -- just link the bundle to the framework of shared stuff.

like image 20
justin Avatar answered Nov 07 '22 04:11

justin