考虑以下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
来编译Host
和LoadablePlugIn
目标的源代码。它编译和工作没有问题,但是,由于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文件夹。