当我们运行iOS项目时,XCode控制台最开始的一堆打印信息提示有库重复,诸如下面的提示信息:
Class AspectsContainer is implemented in both .../ZLDemo.app/Frameworks/Aspects.framework/Aspects (0x10d82cc08) and .../ZLDemo.app/ZLDemo (0x10342bb20) . One of the two will be used. Which one is undefined.
这个报错的意思是同一个类在两个地方都有。先说结论,我们应该清除重复定义的类,对包体积、性能有提升。
一、为什么会出现这个情况
像上面的打印信息意思是在Frameworks/Aspects.framework/Aspects
中和主工程可执行文件ZLDemo
中都有AspectsContainer
这个类。接下来我们去找一下这两个路径下的文件。
首先,打开工程中的Products -> show in finder -> 显示包内容
后可以看到ZLDemo
这个可执行文件,这个是主工程可执行文件。
然后,打开Frameworks/Aspects.framework
可以看到动态库可执行文件Aspects
。
我们会猜测,提示的原因是这个类在主工程的二进制和动态库的二进制都存在,然而事实是这样吗?
接下来,我们在工程中搜索AspectsContainer
, 发现这个类一个是在Aspects
的pod库中,另外一个是在自己的pod私有库AAA
中,也就是说确实是有两份。但打印信息提示的是Aspects
库跟主工程中都有实现,与实际不符。
......
一阵沉默后
我们开始查看私有库AAA
,发现它是静态库,静态库中的符号(符号就是类、方法名、全局变量等)是直接编译进主工程的,这就解释了为什么打印信息提示的是主工程跟Aspects
库有相同类。
注:上述类是OC类,如果是Swfit类,有命名空间(会带有所属Module的名字前缀),不会有这个信息打印。以下也是指的OC的情况。
二、为什么相同的类名不会编译报错
我们先将相同类名
的情况分为2种:在同一个可执行文件、不在同一个可执行文件。并且前提条件是类有实现@implementation
,我试了下,不同库中有相同OC类名,但仅仅只有声明@interface
,编译不会报错,也不会有这个提示。
1.相同的类不在同一个可执行文件
首先,主工程有自己的二进制文件,动态库也有自己独立的二进制文件,不同的二进制文件里面的类会有带有二进制文件名前缀,所以实际并没有重复,不会编译报错。但是Xcode运行时会有上面的提示信息。
这种相同的类名场景包括:动态库与主工程、动态库与动态库、动态库与静态库。
2.相同的类在同一个可执行文件
如果在同一个二进制文件,类名和类名前缀都一样,所以直接编译报错。
我们需要知道的一点是,对于静态库,它的内容会链接到主工程的Mach-O文件中。
这种相同的类名场景包括:静态库和主工程之间,静态库和静态库之间。
我们有时间也可以看一下这篇博客,里面比较对符号冲突讲得更详细,iOS中的符号冲突(一)- 基础原理
三、解决报错-去除重复的类
上面我们已经解释清楚了提示有重复类的原因,接下来解决办法就是,根据情况去掉其中一份Aspects
。
也许你的项目中提示重复的类并不是Aspects
,但解决办法一样。
另外一种查找该类在项目中使用过的地方,可以cd到工程目录后,执行:
// cd到工程目录
grep -r AspectsContainer .
相同报错的其它的情况:
1. 系统的类
如果是系统的类在两个系统的动态库里面都有实现导致报的这个错,比如使用了两个相同功能的系统库中内容(如蓝牙库),我们可以检查自己是否可以改为只依赖一个系统库!如果不能改为一个就请忽略,developer.apple.com/forums/thre…
2. 相同的类在两个动态库中都有一份
根据报错的地址会很容易看出是哪两个库,解决办法是只保留一份即可!
that's all, happy coding!