简体   繁体   中英

How do I handle duplicated symbols in a loadable NSBundle/plug-in?

Consider the following Cocoa project structure:

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. 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. 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:

...
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?

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). Using @rpath makes it easy to share the same library among code that's located at various locations within an app bundle. With @rpath , it becomes the responsibility of module that's doing the loading to specify where the library is in relation to itself. You do this by adding a Runpath Search Path ( LD_RUNPATH_SEARCH_PATHS ) entry for each library the loading module needs.

For the main app executable, it could be either @executable_path/../Frameworks or @loader_path/../Frameworks .

For the plugin, it becomes @loader_path/../../../../Frameworks . (Using @executable_path would not work, since a plugin isn't executed, it's just loaded). 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM