最近使用 Cocoapods 的时候遇到一个问题,就是在 Podfile 中引入 iOS 调试库 FLEX 后,在工程源文件里通过 import FLEX
引入的时候,编译工程报错: No such module 'FLEX’
。
pod 'FLEX', :git => 'https://github.com/FLEXTool/FLEX.git', :branch => 'master'
通过调试检查后发现有两个解决方案:
- Podfile 文件里少了一行语句
use_frameworks!
,加上之后重新pod install
就好了 - 在
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 的方式引入
你可能会问如果不使用 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
的使用规范
- 如果 Podfile 里开启
use_frameworks!
,那就算你在某个 Pod 后面加了:modular_headers => true
也不会生效。 - 没有办法让 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 uponSDWebImage
, 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 setuse_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) 也给出了详细的修改动机。
参考地址: