组件化场景下动态库与静态库依赖分析

A. 主项目使用动态库TestFramework

A1. TestFramework通过Pod依赖三方库(dynamic framework)
现象

编译不报错,运行时报错 dyld[30339]: Library not loaded: @rpath/MBProgressHUD.framework/MBProgressHUD Referenced from: <9FB255F3-CED4-32E5-A9EF-10E473D3BAF6>

根本原因

通过pod依赖的三方动态库无法被动态库TestFramework合并,导致主项目使用时报错;

  • 编译/链接阶段,主项目只需要能找到 TestFramework.framework 里声明的符号即可;
  • TestFramework.framework 的二进制里,引用了 MB/MJ 的符号,但这些符号在 TestFramework 的链接阶段已经解决(因为 TestFramework 已经链接了 MB/MJ)。
  • 主项目编译时不需要直接看到 MB/MJ 的实现,只要 TestFramework 能链接通过,主项目就能编译通过。
解决办法

1、通过构建私有库并且在podspec中配置对MBProgressHUD的依赖,来保证主项目可以正常引用动态库MBProgressHUD; 2、直接把MBProgressHUDMBProgressHUD这两个动态库和TestFramework一起提供给主项目,主项目进行embed &sign in后即可

A2.TestFramework通过Pod依赖三方库(static framework)
现象

主项目可以正常编译运行

根本原因

这主要是因为通过pod依赖的三方静态库(framework类型)被动态库TestFramework所吸收,导致主项目只需要引用动态库TestFramework本身即可,

A3. TestFramework通过Pod依赖三方库(static .a)
现象

主项目可以正常编译运行

根本原因

这主要是因为通过pod依赖的三方静态库(.a类型)被动态库TestFramework所吸收,导致主项目只需要引用动态库TestFramework本身即可,

B. 主项目使用静态库TestFramework

B1. TestFramework 通过 Pod 依赖三方库(static.a)
现象

主项目编译不过,经典报错Undefined symbol: _OBJC_CLASS_$_MBProgressHUD

根本原因

静态框架(Static Framework)在构建时是可以将依赖的静态库代码合并进来的, 具体逻辑是 1、如果是直接链接(在 Link Binary With Libraries 中直接添加.a文件):静态框架会将依赖代码合并进来,这是正常的静态链接行为,此时不需要额外操作,主项目依赖TestFramework可以正常编译运行 2、如果是通过 CocoaPods 间接依赖:CocoaPods 创建的只是一个"依赖声明",实际的代码合并被推迟到最终应用构建时,这是 CocoaPods 的设计选择,不是静态框架的限制

解决办法

1、通过CocoaPods 间接依赖的.a可以通过runscript脚本实现合并.a的符号到framework中

bash 复制代码
FRAMEWORK_BINARY="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/${PRODUCT_NAME}"
MBHUD_LIB="${BUILT_PRODUCTS_DIR}/MBProgressHUD/libMBProgressHUD.a"
MJEXT_LIB="${BUILT_PRODUCTS_DIR}/MJExtension/libMJExtension.a"
# 使用 libtool 直接合并静态库
libtool -static -o "${FRAMEWORK_BINARY}" "${FRAMEWORK_BINARY}" "${MBHUD_LIB}" "${MJEXT_LIB}"

2、修改TestFrameworklink binary with library,将pod生成产物MBProgressHUD.aMJExtension.a加入进去,可能会提示

File is already being linked. Linking "MBProgressHUD.framework" more than once is not supported. To use the same framework for multiple platforms, use an XCFramework.

选择add anyway即可

.a 文件就是一堆对象文件(.o)打包在一起的"压缩包";Xcode/ld 链接器在链接时会递归展开所有 .a 文件,把里面的对象文件合并进最终产物;只要你在 Link Binary With Libraries 里加了 .a,它的内容就会被"吸收"进你的 framework 或可执行文件。

B2. TestFramework通过Pod依赖三方库(static framework)
现象

主项目编译不过,经典报错Undefined symbol: _OBJC_CLASS_$_MBProgressHUD

根本原因

静态框架(Static Framework)在构建时是可以将依赖的静态库代码合并进来的, 具体逻辑是通过 CocoaPods 间接依赖的frameworkCocoaPods 创建的只是一个"依赖声明",实际的代码合并被推迟到最终应用构建时,这是 CocoaPods 的设计选择,不是静态框架的限制

解决办法使用脚本进行合并
bash 复制代码
#!/bin/bash
# 合并静态框架依赖
FRAMEWORK_BINARY="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/${PRODUCT_NAME}"
MBHUD_FRAMEWORK="${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework/MBProgressHUD"
MJEXT_FRAMEWORK="${BUILT_PRODUCTS_DIR}/MJExtension/MJExtension.framework/MJExtension"
# 使用 libtool 合并静态框架
libtool -static -o "${FRAMEWORK_BINARY}" "${FRAMEWORK_BINARY}" "${MBHUD_FRAMEWORK}" "${MJEXT_FRAMEWORK}"

这里不能使用修改TestFramework的link binary with library的方式,因为static framework 其实是一个目录结构,里面有 Headers、Modules、Info.plist,还有一个二进制文件(通常是静态库格式)。但 Xcode 在链接时不会递归处理 framework 里的依赖,它只会把你当前 target 的源文件和你直接加的 .a 文件合并。framework 只是目录结构的封装,Xcode 只会把 framework 作为一个整体链接,不会像处理 .a 那样递归合并里面的内容。我猜主要是Xcode 设计时主要是为动态库(dynamic framework)服务的,动态库的依赖是运行时解决的; .a 是静态链接的"原生食材",Xcode 会把它"煮进锅里";static framework 是"盒饭",Xcode只会把盒饭放在桌上,不会拆开盒饭再煮一遍。

相关代码链接

相关推荐
小墙程序员2 天前
编译原理教程(一)编译器的前端技术
编译原理
苏近之12 天前
如何为 Python 新增语法
python·源码阅读·编译原理
千千寰宇1 个月前
[语法分析/编译原理] Antlr : 开源语法分析工具
编译原理
JNU freshman2 个月前
编译原理实验 之 Tiny C语言编译程序实验 语法分析
编译原理
444A4E2 个月前
C++多态完全指南:从虚函数到底层虚表机制,一文彻底掌握
c++·编译原理
脏脏a2 个月前
程序环境和预处理
c语言·编译原理
l1n3x2 个月前
编译原理前端-词法分析
算法·编译原理
G皮T2 个月前
【Python Cookbook】字符串和文本(五):递归下降分析器
数据结构·python·正则表达式·字符串·编译原理·词法分析·语法解析
444A4E2 个月前
C++模板:泛型编程的魔法手册,从入门到“魔改”
c++·编译原理