IOS开发经典错误:Undefined symbols for architecture XXX

ios 经典错误:Undefined symbols for architecture XXX

因为我是在真机上编译的项目,所以报错的架构(CPU 指令集)为 arm64。
如果我们使用 64 位模拟器(iPhone simulators 5s 或更高版本)编译或者运行项目,就会报以下错误:

1
Undefined symbols for architecture x86_64:

如果我们使用 32 位模拟器(iPhone simulators 5 或更低版本)编译或者运行项目,就会报以下错误:

1
Undefined symbols for architecture i386:

类似的错误是一个开发中经常遇到的问题,凡是涉及到第三方静态库的项目,都不可避免的遇到过这一类错误。本次记录实在我向项目中集成友盟统计是遇到的,下面就以友盟统计为举例

原因之一:没有链接 lib 库而报错

真机编译项目,我们看到两个经典的的错误

1
"OBJC_CLASS$_MobClick", referenced from:

1
linker command failed with exit code 1 (use -v to see invocation)

点击第一个错误,如下图

image.jpg

然后看到 Undefined symbols for architecture arm64:

image.jpg

但是我们怎么知道是没有链接 libMobClickLibrary.a 库而不是其他的什么库呢?这还要取决于这句

1
"_OBJC_CLASS_$_MobClick", referenced from:。

其中”OBJC_CLASS$_MobClick 中的 MobClick”就是我们引用的 libMobClickLibrary.a 中的一个文件。因为我真的是在 AliyunSalesCustomerListTableViewManager.m 中通过#import “MobClick.h”引用了 MobClick.h,如下图:

image.jpg

如果我们引用了 libMobClickLibrary.a 库中的其他文件,那么 OBJC*CLASS$*后面就不是 MobClick 了。
有的时候,因为后面的类名跟第三方的库名没有任何相似处,比如库名叫做 libAAA.a,而报错的类名却是 BBB。此时,我们通过后面的类名根本不能准确的判断出这个 BBB 属于哪个库,也就不知道该链接哪个库。这种情况下,我们可以通过 referenced from:提示后面的文件名来判断 BBB 到底属于哪个静态库,因为我们自己的某个类文件不太可能 import 很多第三方的库,这种情况下,采取这种方式比较好判断。

总结

  • 综上,我们可知:项目中如果用到了某个第三方静态库,但 lib 静态库或者 framework 静态库没有被链接时,就会遇到 Undefined symbols for architecture XXX 这一类的错误。

原因

  • 编译项目时,因为静态库没有链接进工程,所以静态库就不会参与编译,而项目某些文件(.m 文件)又引用(或者说依赖)了静态库,所以自然会报错,而报的错就是经典的 Undefined symbols for architecture XXX 这一类的错误。

解决方案

  • 下次遇到这类问题,我们只需要在 Linked Frameworks and Libraries 中添加指定的静态库即可!

原因之二:没有链接.framework 静态库而报错

上面说明了工程中因为缺少链接 lib 库导致报错的一种情况。iOS 开发中有两种格式的静态库(.a 格式和.framework 格式)。所以,我们也不难猜测:缺少链接.framework 格式的静态库也会导致同样的错误。如果我们引用的第三方库并不是.a 格式的静态库,而是.framework 格式的静态库,在 Linked Frameworks and Libraries 中没有被链接的情况下,也会报同样的错误。比如我在 Linked Frameworks and Libraries 中删除 PushCenterSDK.framework 静态库(这个静态库存在于木纹中,不是 cocoapods 管理的),如下图
模拟器编译项目,出现以下三个错误

image.jpg

点击第一个错误,查看错误详情,如下图:

image.jpg


原因之三:extern 引用不存在的全局变量而报错

开发中,我们很有可能用到全局变量,比如在 delegate.m 文件中定义了一个 int 型全局变量 globalVar,在 ViewController.m 文件中通过 extern int globalVar; 而引用 A.m 文件的这个全局变量。这样是没问题。但是如果我们不小心把 extern int globalVar 写成 extern int globalVariate,且在 ViewController.m 文件中使用了这个 globalVariate 变量(代码如下)

1
2
3
4
5
6
7
8
9
10
11
12
#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

int globalVar; // 生命一个全局变量
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

extern int globalVariate; // 引用一个不存在的全局变量
- (void)viewDidLoad {
[super viewDidLoad];

globalVariate = 10; // 给不存在的全局变量赋值
}

@end
  • 编译上面代码也会报同样的错误,如下图

image.jpg

原因:因为 extern int globalVariate 并没有定义名为 globalVariate 的变量,而是引用了一个名字叫做 globalVariate 的全局变量。当我们使用 globalVariate 时候,系统发现这个变量根本没有定义,就会报这个错误