Cocoapods 使用报错 No such module 'XX'

最近使用 Cocoapods 的时候遇到一个问题,就是在 Podfile 中引入 iOS 调试库 FLEX 后,在工程源文件里通过 import FLEX 引入的时候,编译工程报错: No such module 'FLEX’

pod 'FLEX', :git => 'https://github.com/FLEXTool/FLEX.git', :branch => 'master'

通过调试检查后发现有两个解决方案:

  1. Podfile 文件里少了一行语句 use_frameworks! ,加上之后重新 pod install 就好了
  2. pod ‘FLEX’ 后面加上一句 :modular_headers => true 也能解决编译报错的问题

分析一下这两个解决方案

# use_frameworks! 方式

正常我们 pod init 的时候生成的 Podfile 里面默认都是带 use_frameworks!,Cocoapods 是在 0.36 版本的时候引入 use_frameworks! ,目的通过动态库(framework)而不是静态库(static libraries) 的方式去整合工程。

framework 有可能是静态库的,但当苹果提到 framework 基本上就是暗示是动态库(When Apple and Xcode refer to frameworks, dynamic is typically implied.) 参考这里 https://blog.cocoapods.org/CocoaPods-1.4.0/ (opens new window)

事情背景是 2014 年发的 iOS8 引入开始支持动态库引入,同时发布 Swift 语言,同时苹果不允许构建包含 Swift 代码的静态库。所以我们想要使用 Swift 语言来封装库的话唯一的选择就是生成动态库。所以 Podfile 如果不使用 use_frameworks! 的话,是不能整合包含 Swift 代码的第三方 Pod 库。

苹果不允许构建包含 Swift 代码的静态库的原因是?ABI 没稳定造成后续的兼容问题。

使用 use_frameworks! 之后,系统会生成 FLEX module,工程可以通过直接 import 的形式引入 FLEX module

#FLEX.modulemap
framework module FLEX {
  umbrella header "FLEX-umbrella.h"

  export *
  module * { export * }
}

# :modular_headers ⇒ true 的方式

如果主工程是用 Swift 语言,同时使用 OC 语言编写的库来说,不使用 use_frameworks! 的时候,build 出来的 FLEX 结果是静态库 libFLEX.a,并没有生成 module,所以也就没有办法通过 import 的方式引入,加上 :modular_headers ⇒ true 的时候会在编译相关目录里添加 module 的支持,这样就能继续通过 import 的方式引入

Untitled

你可能会问如果不使用 use_frameworks! 那我有包含 Swift 代码的库咋办呢?苹果从 Xcode9 开始就支持包含 Swift 代码的静态库了(大量的动态库会影响启动速度),Cocoapods 也发布了对应的更新 1.5.0,开发者没有必要一定在 Podfile 里强制使用 use_frameworks!

同时如果我们的工程是 Swift 语言的,使用的库是包含 OC 的代码的话,就必须给包含 OC 的库开启 modular headers 来支持 module,但是 Swift 库不是必须的,因为 Swift 库天生支持 module。在我们的例子里,FLEX 就是 OC 语言的库,所以必须开启 modular headers,开启后效果参考上面的效果图。

多说一句 module,Module 我理解并不是单是 Swift 的概念,而是 LLVM 编译里面的一个概念,module 要解决的问题本质上是传统 C/C++/OC 里面的 #include 导入方式的弊端。通过引入 module 来提升编译效率。所以 Module 本质上就还是和库类似的概念,可能比库覆盖的面更广一些。官方的说法是最小的编译单元,感觉说的还是比较准确的。

[2023-02-21]

补充两个使用 use_frameworks!:modular_headers => true/false 的使用规范

  1. 如果 Podfile 里开启 use_frameworks!,那就算你在某个 Pod 后面加了 :modular_headers => true 也不会生效。
  2. 没有办法让 use_framework 单独对某几个 Pod 生效,参考这里 use_frameworks! for only some pods #3839 (opens new window)

[2023-02-22]

这两天遇到另外一个问题是,我自己私有库 private_lib 中使用到了第三方的库 SDWebImage 这样的 OC 库,在主工程使用如下配置引用 private_lib 库。

#use_frameworks!
pod 'private_lib', :modular_headers => true

执行 pod install 报错信息如下

The Swift pod private_lib depends upon SDWebImage, which do not define modules. To opt into those targets generating module maps (which is necessary to import them from Swift when building as static libraries), you may set use_modular_headers! globally in your Podfile, or specify :modular_headers => true for particular dependencies.

本质上还是 private_lib 里面的 SDWebImage 没有配置 module_headers 的原因。但是如何在私有库中对这项进行配置呢?在搜索答案的过程中,我找到这个 issue: The following Swift pods cannot yet be integrated as static libraries: #784 (opens new window),进而发现了 Cocoapods 的一个配置 use_frameworks! :linkage => :static 这是从 Cocoapods 1.9.0 (opens new window) 引入的,目的是给使用者更多权限去控制 Pods 打包和链接方式。

我自己尝试的结果是当使用 use_frameworks! :linkage => :static 的时候,Pods 都会被打包成静态库但是是 .framework 格式的,而不是 .a 格式的,.framework 格式比 .a 格式可以更好的去管理资源,当然最后也是静态链接到我们的主程序。我们使用这个配置就能不需要再使用 use_modular_headers 了,因为 framework 自动会生成 module。

最终我选择了这种配置方式来进行配置,而不去使用动态库的方式,为了让程序更快的启动。官方的这篇 issue (opens new window) 也给出了详细的修改动机。

参考地址:

  1. CocoaPods 0.36 - Framework and Swift Support (opens new window)
  2. CocoaPods 1.5.0 — Swift Static Libraries (opens new window)
  3. Module System of Swift (简析 Swift 的模块系统) (opens new window) #module 介绍的比较详细
  4. 为什么应该用模块取代C/C++中的头文件? 转 (opens new window)
  5. CocoaPods 1.9 Beta has arrived! (opens new window)