本文涉及的知识点:
- 动态库的创建&使用
- 编译各个架构通用的Framework
- 动态库剥离
1. 创建一个Framework项目&使用
1.1 创建步骤
-
Xcode版本为12.2 -
操作步骤:
Create a new Xcode Project -> iOS -> Framework & Library -> Framework -> next

-
在项目里添加代码,比如
HDLogTool,在DylibFramework中添加代码:#import <HDLogTool.h>,如下:

-
开放共用代码的头文件。把需要暴露给外部的头文件拖拽到
Target -> Build Phases -> Headers -> Public中

-
接下来,选择一个模拟器进行编译(后面会讲到支持各个CPU架构的编译)
1.2 使用它
-
在当前项目中添加一个
Target:File-> New -> Target -> iOS -> Application -> App -
在
Demo中导入动态库

-
接下来,你就可以在
Demo中使用动态库的代码了,并且应该能正确编译执行,如果修改了动态库中的代码,直接运行demo就能看到效果
编译通用架构的 Framework
1.1 在其他项目中使用Framework
- 在上节我通过添加子项目的方式,可以实现动态库的使用,但是在实际开发中,Framework需要单独拿出来提供给开发者们使用。
- 在
Products -> DylibFramework.framework -> Show in finder找到打包的好的库文件。 - 通过命令查看
Framework支持的架构类型为x86_64
# lipo 是mac系统自带的一个工具,可以在终端直接敲入查看使用时的一些参数
lipo -info DylibFramework

- 拖入新建的工程
DylibTest中,在General -> Frameworks,Libararies,and Embedded content中把导入的库设置为Embed & Sign(老版Xcode中,Embed是单独设置的)
Embed & Sign可以理解为:在build时需要拷贝进App Bundle里的库,这是相对苹果官方的动态库而言的,官方提供的系统库是不需要拷贝进App Bundle中的。Sign代表签名,导入到App Bundle中的库在打包上传时需要签名操作
- 在项目中使用后,运行成功

- 当我们选择运行在真机上时,会报错,如图:

Xcode编译Framework时针对模拟器和真机打的包是不一样的,支持的平台自然也不一样
1.2 编译各个平台的Framework
1.2.1 合并
- 编译生成各个平台的动态库。选中任一模拟器编译一下,选中
Any iOS Device编译一下,此时,在沙盒中已经生成了两种类型的动态库 - 使用
Xcode中的Aggregat来完成Framework的合并 - 在
DylibFramework项目中创建一个Aggregat Target:File -> New -> Target -> Other -> Aggregat

- 在
Aggregat的target中新建脚本

- 脚本如下(作用:合并支持模拟器和真机的动态库,并生成在根目录的
Products中):
if [ "${ACTION}" = "build" ]
then
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework
if [ -d "${INSTALL_DIR}" ]x86_64
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
#ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
# 使用lipo命令将其合并成一个通用framework
# 最后将生成的通用framework放置在工程根目录下新建的Products目录下
lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
#open "${DEVICE_DIR}"
# 打开生成的文件夹
open "${SRCROOT}/Products"
fi
- 此时我们可以看到动态库也合并完成。通过命令可以看到支持的CUP类型:

- 我们把动态库拖入项目中,再次运行,又出错了
QAQ
# 因为后来没复现,所以这里只把报错信息发出来
/DylibTest.xcodeproj Building for iOS Simulator, but the linked and embedded framework 'DylibFramework.framework' was built for iOS + iOS Simulator.
- 解决办法:
Build Settings -> Validate Workspace -> YES
参考链接:
https://stackoverflow.com/questions/63267897/building-for-ios-simulator-but-the-linked-framework-framework-was-built
- 修改为
YES后,完美运行。 - 由于
iOS编译的特殊性,为了方便调试,很多SDK将i386(iOS14模拟器已不支持)、x86_64、armv7、arm64几个平台合并到一起了。上传app store时,需要将i386、x86_64两个平台的库删除,否则无法正常提交审核。
1.2.2 剥离
- 主要是通过
lipo命令对库进行一些操作 - 我们先拷贝一份现在的动态库
DylibFramework.framework - 从动态库中剥离出
armv7和arm64的库
# armv7
lipo DylibFramework.framework/DylibFramework -thin armv7 -output DylibFramework_armv7
# arm64
lipo DylibFramework.framework/DylibFramework -thin arm64 -output DylibFramework_arm64
- 将
armv7和arm64的库打包
lipo -create DylibFramework_armv7 DylibFramework_arm64 -output DylibFramework
- 修改文件名
# 把生成的新的库替换掉备份里面的库
mv DylibFramework DylibFramework.framework/
- 把
DylibFramework.framework重新导入项目中,就可以使用了
思考:
- 编译通用版本的动态库,操作相对复杂。可以考虑写一些脚本来支持。
- 制作动态库时需要根据实际的业务来进行代码抽取,建议先私有化,再考虑编译为动态库
- 设计合理的动态库更新方案相对复杂,需要契合现有开发模式。
- 要编写完善的项目开发文档,供后续开发人员使用。
- 目前
CocoaPods支持以动态库的形式集成第三方,同时CocoaPods可以看到代码,便于调试,所以可以优先考虑使用CocoaPods来做。

本文详细介绍如何在iOS中创建并编译支持多种CPU架构的动态库(Framework),包括模拟器和真机环境下的编译流程,以及如何通过lipo命令合并不同架构的库文件。
1980

被折叠的 条评论
为什么被折叠?



