前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS符号冲突(duplicate symbols)

iOS符号冲突(duplicate symbols)

作者头像
落影
发布2020-04-24 10:35:09
4.3K1
发布2020-04-24 10:35:09
举报
文章被收录于专栏:落影的专栏落影的专栏

前言

背景是我们项目升级某个SDK,结果发现项目和SDK出现符号冲突。

符号冲突是接入SDK有可能会出现的问题,本文便尝试从技术角度去解决。

正文

因为本身项目本身不便公开,所以新建两个工程来模拟这个场景。(工程代码地址)

LYTestFramework静态库工程,里面带有不公开的SSUser类,模拟SDK;

LearnSymbol普通工程,模拟项目的主工程,里面也有SSUser类;

将LYTestFramework手动导入LearnSymbol工程:

这样便出现了两个SSUser:

一个是LYTestFramework.framework内不公开的SSUser;

代码语言:javascript
复制
@implementation SSUser
// from framework's user
- (void)test {
    NSLog(@"framework test");
}
@end

另一个是LearnSymbol工程内自己带的SSUser;

代码语言:javascript
复制
@implementation SSUser
// from project's user
- (void)test {
    NSLog(@"main test");
}
@end

那么编译的时候,就会出现duplicate symbol _OBJC_CLASS_$_SSUser的错误。

可是,当我真正开始运行的时候,才发现竟然编译通过了:

对比了这个新建工程和原工程的Other Linker Flags,发现是因为新工程少了一个-ObjC的设置,另外原工程还有-l secXXX的flag。

回顾下-ObjC 、 -all_load 、-force_load这三个flag的区别:

  • -ObjC 链接器会加载静态库中所有的Objective-C类和Category;(导致可执行文件变大)
  • -all_load 链接器会加载静态库中所有的Objective-C类和Category(这里和上面一样);当静态库只有Category时-ObjC会失效,需要使用这个flag;
  • -force_load 加载特定静态库的全部类,与-all_load类似但是只限定于特定静态库,所以-force_load需要指定静态库;当两个静态库存在同样的符号时,使用-all_load会出现duplicate symbol的错误,此时可以选择将其中一个库-force_load;(需要注意两个库的版本是不是一致的)

所以这里的直接编译通过的原因:工程中已经有了SSUser类的符号,所以链接的时候会直接使用工程中的SSUser符号,所以编译运行完的结果是调用了工程中的SSUser类,静态库中的SSUser并没有被链接。

而原工程的-l secXXX的链接flag是什么意思?

gcc有三个很像的参数,分别是-l -l -L,第一个I是i的大写,中间的是L的小写l。

  • -I,用于指定头文件的地址;
  • -l,用于指定具体的静态库、动态库;
  • -L,用于指定库文件的地址;

回到我们的工程,我们往Other Linker Flags添加-ObjC的flag之后,再次尝试编译。

此时终于复现了之前的符号冲突:

代码语言:javascript
复制
duplicate symbol _OBJC_CLASS_$_SSUser in:
    /Users/loyinglin/Library/Developer/Xcode/DerivedData/LearnSymbol-dhlwaeprifzeedegywrvodujmcoj/Build/Intermediates.noindex/LearnSymbol.build/Debug-iphonesimulator/LearnSymbol.build/Objects-normal/x86_64/SSUser.o
    /Users/loyinglin/Documents/Learn/LearnDuplicateSymbol/LearnSymbol/LearnSymbol/LYTestFramework.framework/LYTestFramework(SSUser.o)
duplicate symbol _OBJC_METACLASS_$_SSUser in:
    /Users/loyinglin/Library/Developer/Xcode/DerivedData/LearnSymbol-dhlwaeprifzeedegywrvodujmcoj/Build/Intermediates.noindex/LearnSymbol.build/Debug-iphonesimulator/LearnSymbol.build/Objects-normal/x86_64/SSUser.o
    /Users/loyinglin/Documents/Learn/LearnDuplicateSymbol/LearnSymbol/LearnSymbol/LYTestFramework.framework/LYTestFramework(SSUser.o)
ld: 2 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

接下来从尝试技术的角度去解决这个问题:

解决方案1:去掉主工程的SSUser,用静态库里面的SSUser;

不可行,静态库的SSUser没有开放头文件,无法使用;

解决方案2:在主工程的compile source去掉SSUser.m文件,工程中仅用SSUser.h文件来调用;(假设两方用的是同个版本)

尝试编译,符号冲突可以解决;

运行的结果表示调用了LYTestFramework中的SSUser:

代码语言:javascript
复制
2019-07-14 14:13:21.767218+0800 LearnSymbol[28982:5102302] framework test

解决方案3:去掉LYTestFramework静态库中的SSUser符号,链接时全部使用主工程的SSUser;

我们知道静态库是多个.o文件组成的,那么我们可以找到SSUser.o然后剔除,静态库依赖的SSUser会在链接时找到主工程生成的SSUser.o;

我们先进入打包的出来的LYTestFramework.framework文件夹,目录如下:

我们在Headers的同级目录创建一个目录pack,将LYTestFramework这个文件移动到pack目录中。

ar -t LYTestFramework指令,可以看到这个库中的.o文件包括SSUser.o,下面尝试手动移除这个SSUser.o文件:

  • 1、先将LYTestFramework解压:ar xv LYTestFramework
  • 2、手动删除SSUser.o文件;
  • 3、回到上级目录,重新把.o文件打包:ar rcs LYTestFramework pack/*.o

再用ar -t LYTestFramework指令查看,发现SSUser.o已经不见,重新打包成功运行,结果表示调用了主工程的SSUser:

代码语言:javascript
复制
2019-07-17 16:20:33.576468+0800 LearnSymbol[86290:7683465] main test

附1:这为了简化逻辑,这里只有模拟器的cpu架构,没有包括armv7/arm64,用 lipo -info LYTestFramework指令可以看到: LYTestFramework is architecture: x86_64; 如果有多种cpu架构,需要分别对每种架构进行处理,再合并。 附2:以上的解决方案均是假设两方用的是同个静态库版本。如果是不同版本,则需要修改命名,使得多个版本的静态库可以共存。

另一个Linking中的选项:

Dead Code Stripping 是对程序编译出的可执行二进制文件中没有被实际使用的代码进行Strip操作。

Dead code stripping removes code that the compiler determines is unreachable.

代码举例:

总结

符号冲突是引入第三方库的时候,有可能会遇到的问题。

当库A和库B的符号出现冲突时,如果库A和库B冲突的符号,是功能相同的符号,则可以选择去掉其中一个符号,选择只加载其中一个库的符号。

如果两个符号所表示的意义不同,比如说不来自同一个库(仅仅是命名一样,导致符号冲突),或者来自同一个库但是版本不同,这种只能通过重命名或者修改库的代码逻辑来实现共存。

附录

静态库与动态库的思考

编译与链接过程的思考

https://blog.csdn.net/djl4104804/article/details/43099061

https://garbageout.wordpress.com/2015/03/24/dead-code-stripping/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文
  • 总结
  • 附录
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档