Mic*_*l A 14 cocoa-touch ios swift
我开发了一个Cocoa触摸框架,并且遇到了嵌入其中的第三方静态框架类的问题.
当消费者项目使用我的框架并导入我的框架使用的第三方静态框架时,问题是符号冲突.
我最终想要从我的框架中删除这些类,因为它们与宿主项目类冲突(它们使用相同的第三方框架)并以某种方式告诉我的框架依赖于主项目第三方框架(我将指示开发人员导入框架),或者我会为这些类添加一个前缀,以便在托管项目时嵌入我的框架并使用与我自己的框架相同的第三方框架,它将不会产生符号冲突
任何帮助或指示都将受到欢迎!
CocoaPods可以帮助您解决重复符号的问题.
下面我提供了如何实现它的详细解释:
定义
让我们为更简单的解释做一些定义:
MyFramework- 您正在开发的框架.
MyApplication- 使用的应用程序MyFramework.
OtherFramework- 用于MyFramework和的第三方框架MyApplication.
问题
据我所知,问题是Xcode无法使用"重复符号"错误构建OtherFramework.
解决方案
以下是解决该问题所需满足的条件:
1)MyFramework必须OtherFramework由CocoaPods引用:
// MyFramework Podfile
use_frameworks!
pod "OtherFramework"
Run Code Online (Sandbox Code Playgroud)
2)MyApplication必须OtherFramework由CocoaPods引用:
// MyApplication Podfile
use_frameworks!
pod "OtherFramework"
Run Code Online (Sandbox Code Playgroud)
3)MyApplication可以使用任何机制来引用MyFramework(通过CocoaPods或通过拖放框架来进行项目).
4)OtherFramework必须使用CocoaPods构建.
如果它还没有使用CocoaPods构建,你可以自己制作.
为此目标,您需要创建OtherFramework.podspec并选择将其提交给CocoaPods私有存储库.如果您有源文件或只是OtherFramework.framework捆绑包没关系.有关在此处构建CocoaPod的更多详细信息.
TL; DR
使用动态框架,您不必非常关心它,因为链接器使用合理的默认行为.如果你真的想做你所要求的,你可以指示链接器这样做,冒着运行时失败的风险.请参阅答案的结尾以获得解释.
一般来说,关于静态链接
这是经典"依赖地狱"问题的另一个版本.从理论上讲,静态链接目标文件有两种解决方案:
陈述您的依赖关系,让框架的用户在他们的构建系统中解决它.一些想法:
诸如CocoaPods和Carthage之类的外部系统将帮助您在强制约束到用户的构建系统(即用户可能不使用CocoaPods)的不利方面.
包括它的依赖项和头文件,指示您的用户不要使用您提供的该依赖项版本.缺点当然是您的用户无法在需要时切换实施.
(也许是最简单的).构建两个版本的框架,一个版本没有链接的依赖库.然后用户可以选择使用您提供的版本或他们自己的版本.缺点是没有好的方法来确定他们的版本是否与您的代码兼容.
避免泄露您的依赖关系并将其封装在您的框架中,代价是更大的代码大小.现在,这通常是我的偏好之路,即使在移动设备上,代码大小也不是真正的问题.方法包括:
#define宏重命名符号(或仅使用static符号).这篇文章讨论了不同替代方案的一些优缺点.
使用动态框架时
如果要构建动态框架,最佳做法是"2".以上.具体而言,动态链接将防止重复符号问题,因为您的库可以链接到其第三方库的版本,而不管任何客户端使用的库.
这是一个简单的例子(为简单起见使用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();
}
Run Code Online (Sandbox Code Playgroud)
mylib.c
#include <stdio.h>
void my_dylib_func() {
printf("Now in dylib\n");
third_party_func();
printf("Now exiting dylib\n");
}
Run Code Online (Sandbox Code Playgroud)
thirdparty_v1.c
#include <stdio.h>
void third_party_func() {
printf("Third party func, v1\n");
}
Run Code Online (Sandbox Code Playgroud)
thirdparty_v2.c
#include <stdio.h>
void third_party_func() {
printf("Third party func, v2\n");
}
Run Code Online (Sandbox Code Playgroud)
现在,让我们首先编译文件:
$ clang -c *.c
$ ls *.o
myapp.o mylib.o thirdparty_v1.o thirdparty_v2.o
Run Code Online (Sandbox Code Playgroud)
接下来,生成静态和动态库
$ 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
Run Code Online (Sandbox Code Playgroud)
现在,如果我们使用(隐式)提供的实现静态编译:
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
Run Code Online (Sandbox Code Playgroud)
现在,这对我来说非常令人惊讶,因为我期待一个"重复符号"错误.事实证明,ldOSX默默地替换符号,导致用户的应用程序替换我的库中的符号.原因记录在ld联机帮助页中:
如果需要解析某些符号引用,ld将只从静态库中提取.o文件
这将对应于上面的第1点.(另请注意,在Linux上运行上面的示例肯定会出现"重复符号"错误).
现在,让我们动态链接,例如:
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
Run Code Online (Sandbox Code Playgroud)
如您所见,您的动态库现在引用其静态库的版本(v1),而应用程序本身将使用另一个(v2)版本.这可能是你想要的,这是默认值.原因当然是现在有两个二进制文件,每个二进制文件都有自己的符号集.如果我们检查一下.dylib,我们可以看到它仍然导出第三方库:
$ nm libmydynamic.dylib
0000000000000ec0 T _my_dylib_func
U _printf
0000000000000f00 T _third_party_func
U dyld_stub_binder
Run Code Online (Sandbox Code Playgroud)
当然,如果我们没有链接到我们应用程序中的静态库,链接器将在以下位置找到符号.dylib:
$ clang -omyapp myapp.o -L. -lmydynamic && ./myapp
Hello from app
Third party func, v1
Now in dylib
Third party func, v1
Now exiting dylib
Run Code Online (Sandbox Code Playgroud)
现在,如果我们不想向我们使用某些静态库的应用程序公开,我们可以通过不导出其符号来隐藏它(将其隐藏为不让应用程序意外引用它,而不是真正隐藏它):
$ 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
Run Code Online (Sandbox Code Playgroud)
(大写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)
Run Code Online (Sandbox Code Playgroud)
现在我们已成功将第三方静态框架隐藏到客户端应用程序.请注意,通常您不必关心.
如果你真的想要1.在动态框架中怎么样?
例如,您的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
Run Code Online (Sandbox Code Playgroud)
缺点当然是如果您的客户端无法包含静态库,它将在运行时失败.
| 归档时间: |
|
| 查看次数: |
2989 次 |
| 最近记录: |