SHORT VERSION:
How can I load classes during runtime from a bundle, when those classes inherit from superclasses not defined in the bundle, but from another framework which was loaded prior? When loading my framework, classes contained therein work fine. When loading the plugin afterwards, any classes which inherit from NSObject work fine but classes inheriting from the framework are not being loaded.
如果这些类继承自bundle中未定义的超类,而是从之前加载的另一个框架继承,那么如何在运行时从bundle加载类?加载我的框架时,其中包含的类工作正常。之后加载插件时,从NSObject继承的任何类都可以正常工作,但是没有加载从框架继承的类。
LONG VERSION:
Cocoa allows you to control the Dock icon explicitly via the "Dock Tile Plugin" mechanism. In a nutshell, when your app is added to the Dock, the OS looks inside the app's bundle plugins directory for a bundle called "MyDockTilePlugin.docktileplugin". It then loads this plugin's principal class which must conform to the NSDockTilePlugIn protocol.
Cocoa允许您通过“Dock Tile插件”机制显式控制Dock图标。简而言之,当您的应用程序添加到Dock时,操作系统会在应用程序的bundle plugins目录中查找名为“MyDockTilePlugin.docktileplugin”的包。然后它加载这个插件的主要类,它必须符合NSDockTilePlugIn协议。
I have built such a bundle according to Apple's guide. Now I want to reuse this code by making the component classes superclasses in my custom framework. Subclasses specific to a given project can load the framework.
我根据Apple的指南制作了这样一个包。现在我想通过在我的自定义框架中创建组件类超类来重用此代码。特定于给定项目的子类可以加载框架。
Surprisingly, this is not very straightforward.
令人惊讶的是,这不是很简单。
Here is the process in psuedocode:
这是psuedocode中的过程:
- The docktileplugin is loaded by the Dock.
- In its +initialize class method, the class loads the custom framework, which is set to "optional" in build settings.
- The class then loads its own plugin which contains classes inheriting from framework classes.
- Something like NSLog(@"%@", NSStringFromClass(NSClassFromString(classInheritingFromFramework))); will always display (null)
docktileplugin由Dock加载。
在其+ initialize类方法中,该类加载自定义框架,在构建设置中将其设置为“可选”。
然后该类加载自己的插件,该插件包含继承自框架类的类。
像NSLog(@“%@”,NSStringFromClass(NSClassFromString(classInheritingFromFramework)));将始终显示(null)
How does inheritance work across plugin bundles? THANKS...
继承如何跨插件包工作?谢谢...
If this isn't enough information, please ask for more. I'm trying to be concise and thorough.
如果这还不够,请询问更多信息。我想要简明扼要。
MORE CODE:
+ (void)initialize {
[[NSNotificationCenter defaultCenter] addObserverForName:NSBundleDidLoadNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
NSLog(@"bundle loaded %@", note);
}];
//Plugin bundle
NSBundle *selfBundle = [NSBundle bundleForClass:[self class]];
//Load Custom Framework
NSString *frameworkName = @"Custom.framwork";
NSString *frameworkPath = [[selfBundle bundlePath] stringByAppendingPathComponent:[@"Contents/Frameworks" stringByAppendingPathComponent:frameworkName]];
NSLog(@"Framework %@ at %@", ([[NSFileManager defaultManager] fileExistsAtPath:frameworkPath]? @"EXISTS" : @"DOES NOT EXISTS"), frameworkPath);
if( [[NSBundle bundleWithPath:frameworkPath] load] ) {
NSLog(@"Framework loaded...");
NSLog(@"Framework provided classes such as DockTilePlugin (%@) and UndoManager (%@)", NSStringFromClass(NSClassFromString(@"DockTilePlugin")), NSStringFromClass(NSClassFromString(@"UndoManager")));
} else {
NSLog(@"Error, framework failed to load.");
exit(1);
}
//Load Dock Tile Plugin
NSString *pluginName = [[selfBundle infoDictionary] objectForKey:@"DockTilePluginCoreBundle"];
NSString *pluginPath = [[selfBundle builtInPlugInsPath] stringByAppendingPathComponent:[pluginName stringByAppendingPathExtension:@"bundle"]];
NSLog(@"Plugin %@ at %@", ([[NSFileManager defaultManager] fileExistsAtPath:pluginPath]? @"EXISTS" : @"DOES NOT EXISTS"), pluginPath);
if( [[NSBundle bundleWithPath:pluginPath] load] ) {
NSLog(@"Plugin loaded...");
NSLog(@"Plugin provided classes such as DockTilePlugin (%@) and DockTileView (%@)", NSStringFromClass(NSClassFromString(@"DockTilePlugin")), NSStringFromClass(NSClassFromString(@"DockTileView")));
} else {
NSLog(@"Error, Plugin failed to load.");
exit(1);
}
}
What output I see in the Console.app is that the first two "classes such as" show up (class names appear), but in the second two I get "Plugin Provided classes such as DockTilePlugin (null) and DockTileView (null)".
我在Console.app中看到的输出是前两个“类”如出现(类名出现),但在后两个中我得到“插件提供的类,如DockTilePlugin(null)和DockTileView(null)” 。
Also, the notifications are received for both bundles. In the framework, I see in the "NSLoadedClasses" key that all framework classes are loaded and available. In the Plugin bundle, this list is empty (although there should be several classes from the bundle, but they all inherit from the framework).
此外,还会收到两个捆绑包的通知。在框架中,我在“NSLoadedClasses”键中看到所有框架类都已加载并可用。在Plugin包中,这个列表是空的(尽管bundle中应该有几个类,但它们都是从框架继承的)。
If I add a third class to the plugin, which inherits from Cocoa, that class appears fine. Something about bundle-loading a class which inherits from another bundle-loaded class is tricky. That is the heart of it I think.
如果我向插件添加第三个类,它继承自Cocoa,那么该类似乎很好。关于bundle-loading一个继承自另一个bundle-loading类的类的东西很棘手。我认为这就是它的核心。
1 个解决方案
#1
1
Mike from http://mikeash.com - an amazing resource - provided me with the tip that solved this problem. The cause of the problem eludes me, but it was fixed by doing two things:
来自http://mikeash.com的迈克 - 一个惊人的资源 - 为我提供了解决这个问题的技巧。问题的原因使我无法解决,但通过做两件事来解决这个问题:
- In the Framework's project, setting its build location to @rpath as described in this article: http://mikeash.com/pyblog/friday-qa-2009-11-06-linking-and-install-names.html
- In the plugins loading the framework, weak reference during build, don't copy the framework during build, and then set the -rpath build setting to the location of the framework in the containing app. This ensures all items are using the same copy of the framework.
在Framework的项目中,如本文所述,将其构建位置设置为@rpath:http://mikeash.com/pyblog/friday-qa-2009-11-06-linking-and-install-names.html
在加载框架的插件中,构建期间的弱引用,在构建期间不复制框架,然后将-rpath构建设置设置为包含应用程序中框架的位置。这可确保所有项目都使用相同的框架副本。
#1
1
Mike from http://mikeash.com - an amazing resource - provided me with the tip that solved this problem. The cause of the problem eludes me, but it was fixed by doing two things:
来自http://mikeash.com的迈克 - 一个惊人的资源 - 为我提供了解决这个问题的技巧。问题的原因使我无法解决,但通过做两件事来解决这个问题:
- In the Framework's project, setting its build location to @rpath as described in this article: http://mikeash.com/pyblog/friday-qa-2009-11-06-linking-and-install-names.html
- In the plugins loading the framework, weak reference during build, don't copy the framework during build, and then set the -rpath build setting to the location of the framework in the containing app. This ensures all items are using the same copy of the framework.
在Framework的项目中,如本文所述,将其构建位置设置为@rpath:http://mikeash.com/pyblog/friday-qa-2009-11-06-linking-and-install-names.html
在加载框架的插件中,构建期间的弱引用,在构建期间不复制框架,然后将-rpath构建设置设置为包含应用程序中框架的位置。这可确保所有项目都使用相同的框架副本。