如何解决Cocoa touch框架中的符号名称冲突

我开发了一个Cocoa触摸框架,并且在第三方静态框架类中embedded了它的内部问题。

问题是消费者项目使用我的框架时的符号冲突,也导入我的框架使用的第三方静态框架。

我最终希望从我的框架中删除这些类,因为它们与主机项目类(他们使用相同的第三方框架)相冲突,并以某种方式告诉我的框架依赖主项目第三方框架(我将指示开发人员导入框架),或者,我会为这些类添加一个前缀,以便在托pipe项目中embedded我的框架并使用与我自己的框架相同的第三方框架时,不会遇到符号冲突

任何帮助或方向将受到欢迎!

CocoaPods可以帮助你解决重复符号的问题。
下面我提供了有关如何实现的详细解释:

定义
让我们对简单的解释做一些定义:
MyFramework – 你正在开发的框架。
MyApplication – 使用MyFramework应用程序。
OtherFrameworkMyFrameworkMyApplication使用的第三方框架。

问题
据我所知,问题是Xcode在OtherFramework无法与“重复的符号”错误OtherFramework


以下是您需要满足以解决该问题的条件:

1) MyFramework必须引用OtherFramework的OtherFramework:

 // MyFramework Podfile use_frameworks! pod "OtherFramework" 

2) MyApplication必须引用OtherFramework的OtherFramework:

 // MyApplication Podfile use_frameworks! pod "OtherFramework" 

3) MyApplication可以使用任何机制来引用MyFramework (通过CocoaPods或通过拖放框架来投影)。

4)其他OtherFramework必须与CocoaPodsbuild立。
如果它不是用CocoaPods构build的,那么你可以自己做。
为了实现这个目标,你需要创buildOtherFramework.podspec并且可以select将其提交给CocoaPods私有存储库。 不要紧,如果你有源文件或只是OtherFramework.framework包。 在这里build立cocoa豆的更多细节。

TL; DR

使用dynamic框架,你不应该关心它,因为链接器使用了一个合理的默认行为。 如果你真的想要做你所要求的,你可以指示链接器这样做,在运行时有失败的风险。 看到答案的最后解释。

一般来说,关于静态链接

这是经典的“依赖地狱”问题的另一个版本。 从理论上说,静态链接目标文件有两个解决scheme:

  1. 陈述你的依赖关系,让你的框架的用户在他们的构build系统中解决它。 一些想法:

    • 像CocoaPods和Carthage这样的外部系统可以帮助你强制约束用户的构build系统(即用户可能不使用CocoaPods)。

    • 包括它的依赖和头文件,指导你的用户不要使用你提供的那个依赖的版本。 不利的一面是,你的用户无法在需要的时候切换实现。

    • (也许是最简单的)。 构build您的框架的两个版本,一个与未链接的依赖库。用户然后可以select使用您提供的版本或自己的。 缺点是没有好的方法来确定他们的版本是否与你的代码兼容。

  2. 避免泄漏你的依赖并将其封装在你的框架中,代价是代码的大小。 现在,这通常是我的偏好path,即使在移动设备上,代码大小也不是真正的问题。 方法包括:

    • 在项目中包含依赖项作为源文件。 使用#definemacros来重命名符号(或仅使用static符号)。
    • 从源代码构build依赖关系,在代码预生成(或closures)步骤中重命名符号。
    • 按照原来的方式构build一切,然后在构build的库中重命名“内部”符号。 这可能会导致bug,特别是因为swift / obj-c有dynamic的方面。 看到这个问题 ,例如。

这篇文章讨论了一些不同select的优缺点。

在使用dynamic框架时

如果你正在build立一个dynamic框架,最好的做法是做“2”。 以上。 具体而言,dynamic链接将防止重复的符号问题,因为无论客户端使用哪个库,您的库都可以链接到其第三方库的版本。

下面是一个简单的例子(为简单起见,使用C,但应该适用于Swift,ObjC,C ++或者任何使用ld链接的东西):

另外请注意,我假设你的第三方库是用C / objC / C ++编写的,因为Swift类可以(AFAIK)不在静态库中。

myapp.c

 #include <stdio.h> void main() { printf("Hello from app\n"); third_party_func(); my_dylib_func(); } 

mylib.c

 #include <stdio.h> void my_dylib_func() { printf("Now in dylib\n"); third_party_func(); printf("Now exiting dylib\n"); } 

thirdparty_v1.c

 #include <stdio.h> void third_party_func() { printf("Third party func, v1\n"); } 

thirdparty_v2.c

 #include <stdio.h> void third_party_func() { printf("Third party func, v2\n"); } 

现在,我们先编译这些文件:

 $ clang -c *.c $ ls *.o myapp.o mylib.o thirdparty_v1.o thirdparty_v2.o 

接下来,生成静态和dynamic库

 $ ar -rcs libmystatic.a mylib.o thirdparty_v1.o $ ld -dylib mylib.o thirdparty_v1.o -lc -o libmydynamic.dylib $ ls libmy* | xargs file libmydynamic.dylib: Mach-O 64-bit dynamically linked shared library x86_64 libmystatic.a: current ar archive random library 

现在,如果我们使用(隐式)提供的实现静态编译:

 clang -omyapp myapp.o -L. -lmystatic thirdparty_v2.o && ./myapp Hello from app Third party func, v1 Now in dylib Third party func, v1 Now exiting dylib 

现在,这对我来说是相当惊奇的,因为我期待着一个“重复的符号”的错误。 事实certificate,OSX上的ld默默地取代了这些符号,导致用户的应用程序replace了我的库中的符号。 原因在ld有logging:

如果需要parsing一些符号引用,ld只会将.o文件从静态库中提取出来

这将对应于上面的第1点。 (注意,在Linux上运行上面的例子肯定会给出一个“重复符号”的错误)。

现在,让我们以你的例子dynamic链接:

 clang -omyapp myapp.o -L. -lmydynamic thirdparty_v2.o && ./myapp Hello from app Third party func, v2 Now in dylib Third party func, v1 Now exiting dylib 

如您所见,dynamic库现在引用静态库的版本(v1),而应用程序本身将使用其他版本(v2)。 这可能是你想要的,这是默认的 。 原因当然是现在有两个二进制文件,每个都有自己的一组符号。 如果我们检查.dylib ,我们可以看到它仍然导出第三方库:

 $ nm libmydynamic.dylib 0000000000000ec0 T _my_dylib_func U _printf 0000000000000f00 T _third_party_func U dyld_stub_binder 

当然,如果我们不链接到我们的应用程序中的静态库,链接器将在.dylibfind符号:

 $ clang -omyapp myapp.o -L. -lmydynamic && ./myapp Hello from app Third party func, v1 Now in dylib Third party func, v1 Now exiting dylib 

现在,如果我们不想暴露给应用程序,我们使用一些静态库,我们可以通过不导出其符号来隐藏它(隐藏它,因为不让应用程序意外引用它,而不是真正隐藏它):

 $ ld -dylib mylib.o -unexported_symbol '_third_party_*' thirdparty_v1.o -lc -o libmydynamic_no3p.dylib $ nm -A libmydyn* ... libmydynamic.dylib: 0000000000000f00 T _third_party_func libmydynamic_no3p.dylib: 0000000000000f00 t _third_party_func 

(大写字母T表示符号是公开的,小写字母t表示符号是私有的)。

我们再来看看最后一个例子:

 $ clang -omyapp myapp.o -L. -lmydynamic_no3p && ./myapp Undefined symbols for architecture x86_64: "_third_party_func", referenced from: _main in myapp.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) 

现在我们已经成功地将第三方静态框架隐藏到客户端应用程序中 请注意,通常情况下,您不必在意。

如果你真的想在dynamic框架中做什么呢?

例如,您的lib可能需要客户端提供的第三方库的确切版本。

当然也有一个链接器标志: -undefined dynamic_lookup

 $ ld -dylib mylib.o -undefined dynamic -lc -o libmydynamic_undef.dylib $ clang -omyapp myapp.o -L. -lmydynamic_undef thirdparty_v2.o && ./myapp Hello from app Third party func, v2 Now in dylib Third party func, v2 Now exiting dylib 

不利的一面是,如果你的客户端没有包含静态库,它将在运行时失败。

你总是可以像这样使用框架中的类:

 import Alamofire let request = Alamofire.Request(...) 

如果您在自己的框架中有Request类,则可以按照相同的方式使用它:

 import YourFramework let request = YourFramework.Request(...) 

那里不会有任何冲突。

我以前有过类似的问题,但我使用Objective-C第三方框架。 这个问题是通过使用桥接头特别导入框架拖放到消费者项目,并离开你的框架sorting的封装解决。 这只是我的经验,所以它可能不适用于你的情况,但也可以在这里分享,以防万一它有帮助。