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

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只会把盒饭放在桌上,不会拆开盒饭再煮一遍。

相关代码链接

相关推荐
无厘头三四十4 天前
现代编译器直接生成机器码的原理
编译原理
猪哥帅过吴彦祖7 天前
从源码到可执行文件:揭秘程序编译与执行的底层魔法
操作系统·编译原理·编译器
poemyang14 天前
性能优化之母:为什么说“方法内联”是编译器优化中最关键的一步棋?
java虚拟机·编译原理·即时编译器·方法内联
poemyang17 天前
new出来的对象,不一定在堆上?聊聊Java虚拟机的优化技术:逃逸分析
java虚拟机·编译原理·逃逸分析·即时编译器
前端缘梦18 天前
解锁webpack核心技能(三):从源代码到打包产物编译过程的原理指南
webpack·编译原理·前端工程化
poemyang18 天前
解锁硬件潜能:Java向量化计算,性能飙升W倍!
java虚拟机·编译原理·jit·向量化计算·smid
poemyang19 天前
Java编译器优化秘籍:字节码背后的IR魔法与常见技巧
java虚拟机·编译原理·ir·即时编译器
poemyang20 天前
“代码跑着跑着,就变快了?”——揭秘Java性能幕后引擎:即时编译器
java·java虚拟机·编译原理·jit·即时编译器
poemyang21 天前
“同声传译”还是“全文翻译”?为何HotSpot虚拟机仍要保留解释器?
java·java虚拟机·aot·编译原理·解释执行
漂流瓶jz23 天前
JavaScript语法树简介:AST/CST/词法/语法分析/ESTree/生成工具
前端·javascript·编译原理