One of the two will be used. Which one is undefined.

1,083 阅读4分钟

当我们运行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!