kstenerud / iOS-Universal-Framework

An XCode project template to build universal frameworks (arm7, arm7s, and simulator) for iOS / iPhone.
2.95k stars 473 forks source link

Using framework classes in NIB files #9

Closed kringelbach closed 12 years ago

kringelbach commented 12 years ago

When using a class from a Real Framework in a NIB, I get the following error when running the application:

Unknown class MyFrameworkClass in Interface Builder file.

MyFrameworkClass is included in the NIB file as an Object with Custom Class set to MyFrameworkClass. The actual case is that I have implemented a UITextFieldDelegate that is attached to a UITextField. I.e. I do not need any references to it in my source code. The problem persists even if I connect it to an IBOutlet in my View Controller.

If I use the class from my source code, either by allocating an instance or by implementing _keepAtLinkTime as described in http://stackoverflow.com/questions/1725881/unknown-class-myclass-in-interface-builder-file-error-at-runtime everything works fine. This is however not a user friendly interface to the users of the framework.

Are there any better ways to ensure that all framework items used in a NIB is included in the binary? A solution that always includes the entire framework in the binary is not desirable either.

kstenerud commented 12 years ago

This is really a linker optimization issue, so any solution will have to involve tricking the linker into keeping that class rather than dropping it.

I did the following in a framework target:

I then put the following into the final project:

MyVC* vc = [[MyVC alloc] initWithNibName:NSStringFromClass([MyVC class]) bundle:nil];
[self.view insertSubview:vc.view atIndex:0];

This, of course, causes the "Unknown class" failure since the linker optimizes MyTextField away.

I added the following to MyTextField:

+ (void) forceLinkerLoad_
{
}

And added the following to MyVC (which is also in the framework):

+ (void) initialize
{
    [super initialize];
    [MyTextField forceLinkerLoad_];
}

Since MyVC is going to be directly referenced in code by the final project, adding a static reference to MyTextField means that anything that causes linking of MyVC also causes linking of MyTextField, with no special action required by the user of the framework.

jweinberg commented 12 years ago

The other solution is to use -ObjC -all_load in your Other Linker Flags. This will force it to load all classes from the lib.

kstenerud commented 12 years ago

You could do that, but doing so will cause bloat in the app's final binary since it loads EVERYTHING, including things you're not using. As well, it's something that users of your framework would have to add to their projects, which is not as clean as having things just work out of the box.

This is somewhat related to another linker optimization problem involving source files that contain only categories and no classes. In this case, the linker will drop the categories and the app will fail if it calls any code in that category. You can get around this by making an empty class in the file. I use a macro to make it easier: https://github.com/kstenerud/Objective-Gems/blob/master/Objective-Gems/LoadableCategory.h

jweinberg commented 12 years ago

Yup, I find the bloat to be minimal though, never really had it make or break a project. The whole system is slightly broken and annoying.

kringelbach commented 12 years ago

Thanks for your reply. In my case, I do not always have the view controller as part of the framework, but I will use your suggestion wherever possible. I would rather avoid including all files from the library as suggested in the other comments, but I can experiment with the file sizes and reconsider.

2011/6/17 kstenerud < reply@reply.github.com>

This is really a linker optimization issue, so any solution will have to involve tricking the linker into keeping that class rather than dropping it.

I did the following in a framework target:

  • Make a custom UIViewController subclass with xib (called MyVC)
  • Make a custom UITextField (called MyTextField)
  • Add a text field into MyVC via the interface builder, using custom class MyTextField
  • Put these into a framework and put the resulting embeddedframework into another project

I then put the following into the final project:

MyVC* vc = [[MyVC alloc] initWithNibName:NSStringFromClass([MyVC class]) bundle:nil]; [self.view insertSubview:vc.view atIndex:0];

This, of course, causes the "Unknown class" failure since the linker optimizes MyTextField away.

I added the following to MyTextField:

  • (void) forceLinkerLoad_ { }

And added the following to MyVC (which is also in the framework):

  • (void) initialize { [super initialize]; [MyTextField forceLinkerLoad_]; }

Since MyVC is going to be directly referenced in code by the final project, adding a static reference to MyTextField means that anything that causes linking of MyVC also causes linking of MyTextField, with no special action required by the user of the framework.

Reply to this email directly or view it on GitHub:

https://github.com/kstenerud/iOS-Universal-Framework/issues/9#issuecomment-1388437

kringelbach commented 12 years ago

One more note. I have not seen this problem when using the standard iOS framework, so they must somehow be handled differently.

EDIT: And this is of course because the iOS standard frameworks are part of the OS and loaded dynamically on the device. I will close this issue. Thanks for your suggestions.

Niko-r commented 8 years ago

Hi,

Almost 5 years after your answer, I just want to thank you @kstenerud for sharing your solution with forceLinkerLoad_ .

This was the solution to my issue, I found it nowhere else from here, and you deserve my thanks.

So thank you again ! :-)

paleozogt commented 7 years ago

Another possibility is to use Xcode's Perform Single-Object Prelink