如何处理可加载NSBundle/插件中重复的符号?



考虑以下Cocoa项目结构:

Host
|-- LoadablePlugIn
|   |-- Main.storyboard
|   |-- Info.plist
|   |-- TDWLPIClassOne.h
|   |-- TDWLPIClassOne.m
|   |-- TDWLPIClassTwo.h
|   |-- TDWLPIClassTwo.m
|-- TDWAppDelegate.h
|-- TDWAppDelegate.h
|-- TDWClassOne.h
|-- TDWClassOne.m
|-- TDWUtils.h
|-- TDWUtils.mm
|-- Base.lproj
|   `-- Main.storyboard
|-- Info.plist
`-- main.m

这里的根文件夹指的是Host目标(主可执行目标)的源代码,而LoadablePlugIn指的是具有相应名称的嵌入式插件目标的资源。两个目标同时都想使用TDWUtils符号来满足自己的目的,所以我添加了TDWUtils.mm来编译HostLoadablePlugIn目标的源代码。它编译和工作没有问题,但是,由于LoadablePlugIn应该在运行时加载,链接器无法在二进制文件中找到TDWUtils.mm的重复符号,我不确定这是否是一个健壮的场景:

...
Class plugInPrincipalClass = [NSBundle bundleWithURL:loadablePlugInURL].principalClass;
NSWindowController *windowController = [plugInPC instantiateInitialController];
...

我应该用隐藏符号编译器标志(如-fvisibility=hidden)编译LoadablePlugIn或使用任何其他技术来防止名称冲突,或者我可以让它保持不变,因为在两个二进制文件中TDWUtils的符号具有完全相同的实现?

理想情况下,您可以将重复的代码分解到一个框架中,该框架由需要它的每个模块链接。

所以,在最后的应用程序中,你会有一个像这样的结构:
AppName.app/Contents/MacOS/AppName
AppName.app/Contents/Frameworks/TDWUtils.framework
AppName.app/Contents/PlugIns/Loadable.plugin/Contents/MacOS/Loadable

TDWUtils.framework的动态库安装名库(DYLIB_INSTALL_NAME_BASE)应该是@rpath(即避免在安装名中使用@executable_path@loader_path)。使用@rpath可以很容易地在位于应用包内不同位置的代码之间共享相同的库。在@rpath中,指定库相对于自身的位置成为加载模块的责任。为此,您可以为加载模块需要的每个库添加一个运行路径搜索路径(LD_RUNPATH_SEARCH_PATHS)条目。

对于主应用程序可执行文件,它可以是@executable_path/../Frameworks@loader_path/../Frameworks

对于插件,它变成了@loader_path/../../../../Frameworks。(使用@executable_path将不工作,因为插件不执行,它只是加载)。在运行时,@loader_path在这种情况下变成了AppName.app/Contents/PlugIns/Loadable.plugin/Contents/MacOS/Loadable,所以要进入Frameworks目录,你必须向上爬4层到达Contents文件夹,然后再回到Frameworks文件夹。

相关内容

  • 没有找到相关文章

最新更新