[英]How do I handle duplicated symbols in a loadable NSBundle/plug-in?
Consider the following Cocoa project structure:考虑以下 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
Here the root folder refers to sources of the Host
target (the main executable target), while LoadablePlugIn
refers to resources of an embedded plug-in target with corresponding name.这里的根文件夹是指Host
目标(主要可执行目标)的资源,而LoadablePlugIn
是指具有相应名称的嵌入式插件目标的资源。 Both targets at the same time want to use TDWUtils
symbols for their own purposes, so I add TDWUtils.mm
to compile sources of both Host
and LoadablePlugIn
targets.两个目标同时希望将TDWUtils
符号用于它们自己的目的,因此我添加TDWUtils.mm
来编译Host
和LoadablePlugIn
目标的源代码。 It compiles and works without issues, however since the LoadablePlugIn
is supposed to load during run-time the linker is not able to locate duplicated symbols of TDWUtils.mm
in the binaries and I'm not sure if that is a robust scenario:它编译和工作没有问题,但是由于LoadablePlugIn
应该在运行时加载 linker 无法在二进制文件中找到TDWUtils.mm
的重复符号,我不确定这是否是一个可靠的场景:
...
Class plugInPrincipalClass = [NSBundle bundleWithURL:loadablePlugInURL].principalClass;
NSWindowController *windowController = [plugInPC instantiateInitialController];
...
Should I compile LoadablePlugIn
with hidden symbols compiler flag (like -fvisibility=hidden
) or use any other technique to prevent the name collision or can I just leave it as is because in both binaries the symbols of TDWUtils
have exactly the same implementation?我应该用隐藏符号编译器标志(如-fvisibility=hidden
)编译LoadablePlugIn
还是使用任何其他技术来防止名称冲突,或者我可以保持原样,因为在两个二进制文件中, TDWUtils
的符号具有完全相同的实现?
Ideally, you could factor out the duplicate code into a framework that is linked against by each module that requires it.理想情况下,您可以将重复代码提取到一个框架中,该框架由需要它的每个模块链接。
So, in the final app, you'd have a structure like the following:因此,在最终的应用程序中,您将拥有如下结构:
AppName.app/Contents/MacOS/AppName
AppName.app/Contents/Frameworks/TDWUtils.framework
AppName.app/Contents/PlugIns/Loadable.plugin/Contents/MacOS/Loadable
The Dynamic Library Install Name Base ( DYLIB_INSTALL_NAME_BASE
) for the TDWUtils.framework
should be @rpath
(ie avoid using @executable_path
or @loader_path
in the install name). @rpath
的动态库安装名称库 ( DYLIB_INSTALL_NAME_BASE
) 应该是TDWUtils.framework
(即避免在安装名称中使用@executable_path
或@loader_path
)。 Using @rpath
makes it easy to share the same library among code that's located at various locations within an app bundle.使用@rpath
可以轻松地在位于应用程序包内不同位置的代码之间共享相同的库。 With @rpath
, it becomes the responsibility of module that's doing the loading to specify where the library is in relation to itself.使用@rpath
,进行加载的模块有责任指定库相对于自身的位置。 You do this by adding a Runpath Search Path ( LD_RUNPATH_SEARCH_PATHS
) entry for each library the loading module needs.为此,您可以为加载模块需要的每个库添加一个运行路径搜索路径 ( LD_RUNPATH_SEARCH_PATHS
) 条目。
For the main app executable, it could be either @executable_path/../Frameworks
or @loader_path/../Frameworks
.对于主应用程序可执行文件,它可以是@executable_path/../Frameworks
或@loader_path/../Frameworks
。
For the plugin, it becomes @loader_path/../../../../Frameworks
.对于插件,它变为@loader_path/../../../../Frameworks
。 (Using @executable_path
would not work, since a plugin isn't executed, it's just loaded). (使用@executable_path
是行不通的,因为插件没有被执行,它只是被加载)。 At runtime, @loader_path
in this case becomes AppName.app/Contents/PlugIns/Loadable.plugin/Contents/MacOS/Loadable
, so to get to the Frameworks directory, you have to go up 4 levels to get to the Contents, folder, then back down into the Frameworks folder.在运行时, @loader_path
在这种情况下变为AppName.app/Contents/PlugIns/Loadable.plugin/Contents/MacOS/Loadable
,因此要进入 Frameworks 目录,您必须 go 向上 4 个级别才能到达 Contents 文件夹,然后返回到 Frameworks 文件夹。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.