iOS 最全动态库/静态库制作 Framework/.a
一、静态库
1.创建静态库工程
Xcode -> Create a new Xcode project -> iOS -> Static Library
image.png
2.将代码导入工程 选择Create groups(逻辑路径)
image.png
3.配置一些设置
(1)Build Active Architecture Only
Build Active Architecture Only 设置为NO的意思是当前打包的.a支持所有的设备.否则打包时只能用当前版本的模拟器或真机运行.
image.png
(2)Dead Code Stripping
Build Settings中Link下面的Dead Code Stripping设置为NO:先不进行死代码剥离。
image.png
(3)设置.a最低支持的版本
image.png
3.暴露头文件(.h)以供SDK使用者调用
(1)Bulid Phases -> Copy Files -> + 将所有.h Copy进工程
image.png
(2.1)创建Headers
image.png
(2.2)Headers -> + 导入所有头文件
image.png
(2.3)将所有需要暴露的头文件拖到public
image.png
4.在模拟器和真机中run四种模式
① debug 模式 模拟器运行
② debug 模式 真机运行
③ release 模式 模拟器运行
④ release 模式 真机运行
(1)X86_64 和 arm64 切换
使用模拟器运行,制作的SDK是X86_64
使用真机运行,制作的SDK是arm64
(2)编译配置
苹果架构分为x86_64(模拟器) arm64
苹果包分为 Debug 和 Release
一般都是使用arm64
调试时,使用Debug包,上线使用Release
(1)Debug 和 Release 切换
Release: 选中工程->Edit Scheme->Run->Build Configuration->Release
image.png
image.png
(2)Run工程获取SDK
Clean: Product -> Clean Build Folder
Run工程后,在Product->Show Bulid Folder in Finder -> Products 中获取SDK
通过Product找到位置:
image.png
静态库是以.a后缀结尾,动态库是以.framework 后缀结尾
使用真机运行可能出现的问题:
image.png
这是由于真机的系统版本低于工程设置的最低版本
image.png
5.模拟器和真机通用架构模式合并
(1)查看debug真机和模拟器架构
lipo -info 路径
a.查看debug真机
image.png
b.查看debug模拟器
image.png
(2)移除模拟器包含arm64架构
在合并模拟器和真机架构时,如果.a中都包含arm64架会导致真机和模拟器无法合并。报错:have the same architectures (arm64) and can't be in the same fat output file。
XCode12之前:
编译模拟器静态库支持i386 x86_64两架构
编译真机静态库支持armv7 arm64两架构
使用lipo -create -output命令可以将两个库合并成一个支持模拟器和真机i386 x86_64 armv7 arm64四种架构的胖子库。
然而XCode12编译的模拟器静态库也支持了arm64,导致出现真机库和模拟器库不能合并的问题。
解决方案1:
如果有静态库工程:Build Settings -> Excluded Architectures里按照这样设置一下,再编译合并就不会报错了。
image.png
解决方案2:如果手里只有.a或.framework文件
使用lipo remove命令将模拟器库的arm64架构移除
lipo 模拟器路径.a -remove arm64 -output 模拟器路径.a
deDerivedoate.png
(3)合并模拟器和真机架构
a.终端方式
sudo lipo -create [模拟器路径] [真机路径] -output [合成路径/文件名(自定义)]
合并成功如下:
image.png
查看.a支持设备架构 模拟器+真机
image.png
b.脚本方式
(1)通过target创建 Aggregate
TARGETS.png
Multiplstform.png
(2)新建New Run Script Phase
Resource Tags Build Settings Build Phases Build Rules.png
(3).a合并脚本
# Type a script or drag a script file from your workspace to insert its path.
if [ "${ACTION}" = "build" ]
then
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.a
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.a
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.a
# 如果真机包或模拟包不存在,则退出合并
if [ ! -d "${DEVICE_DIR}" ] || [ ! -d "${SIMULATOR_DIR}" ]
then
exit 0
fi
# 如果合并包已经存在,则替换
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
# 使用lipo命令将其合并成一个通用.a
# 最后将生成的通用.a放置在工程根目录下新建的Products目录下
lipo -create "${DEVICE_DIR" "${SIMULATOR_DIR}" -output "${INSTALL_DIR}"
#合并完成后打开目录
open "${SRCROOT}/Products"
fi
如下图:
Type soript or drop a soript f1le ro Ur workspase to 1nsert t5 path.png
写好脚本后执行run,会自动在在Product->Show Bulid Folder in Finder -> Products 生成静态库。
6.使用静态库
(1)文件(.h、.m)中封装好方法以及合并好生成的.a静态库
image.png
image.png
image.png
(2)将.h文件和.a库拖到需要集成的项目中
image.png
(3)将.h文件和.a库加到Build Phases中
a.将frameWorkTest1加到Compile Sourceas中
image.png
B.生成的.a静态库加在Link Binary With Libaries
image.png
(4)调用静态库方法
image.png
image.png
image.png
可能遇到的问题
报错2 duplicate symbols for architecture arm64
解决方案:
2 duplicate symbols for architecture arm64 常见问题,是在导入第三方SDK的时候出现的。
究其原因一定是SDK中包含了工程中原本存在的某个类:全文搜索找到此类,无用的话将其删除,有用改变名字即可。
二、动态库
1.创建动态库工程
Xcode -> Create a new Xcode project -> iOS -> Framework
Pasted Graphic 7.png
2.将代码导入工程 选择Create groups(逻辑路径)
ehoose eplions for adding these fikes.png
(1)本例创建动态库的名字为dynamicFrameWork,项目会自动生成一个名为dynamicFrameWork的头文件
dynamicFrameWork.png
w Created by Se sn 2921129.png
(2)在framework自动生成的dynamicFrameWork.h文件中导入你想用的头文件,如下图:
wimport &FoundationFoundation.png
(3)不在framework自动生成的dynamicFrameWork.h文件中声明头文件,在新工程使用时会报警告,如下图:
aimport “ViewController.h®.png
1.虽然编译没问题,但作为一个强迫症患者,还是在自动生成的.h文件中把需要用到的头文件全部导入。2.使用时更加方便,只要#import <dynamicFrameWork/dynamicFrameWork.h>即可使用动态库中的包含的全部文件,更加节省代码。
大家可自定义自己的UI界面、功能或逻辑。代码写完后,可在路径TARGETS->Build Phases->Headers下将你需要暴露给外面调用的头文件添加到Public下面即可将文件拖入工程(步骤4中会说)
3.配置一些设置
(1)Build Active Architecture Only
Build Active Architecture Only 设置为NO的意思是当前打包的.a支持所有的设备.否则打包时只能用当前版本的模拟器或真机运行.
Basic.png
(2)Dead Code Stripping
Build Settings中Link下面的Dead Code Stripping设置为NO:先不进行死代码剥离。
Basic Customized.png
(3)Mach-O Type(库性质)
简单总结为
库性质决定库加载到运行时内存的时机、加载的库范围、库的共享权限
mach -O Type:一种文件格式,Mach Object文件格式,是Mac上的可执行文件类型 或者 目标文件(.o)。
以开发者制作三方库的场景为例解释:
Executable: `静态库`,输出二进制
Dynamic Library:`动态非共享库`,输出动态链接库非共享库,程序`运行`时链接到`内存`,大部分场景下不可共享;app extension、部分macOS场景下可以共享
Bundle:`动态非共享库`,和Dynamic Library相近,不过需要手动调用函数加载
Static Library: `静态库`,输出静态链接库,程序`编译`时拷贝到`内存`
Relocatable Object File:`静态库`,和Static Library类似,但体积更小
从结构上看:
1.Framework格式的库只是比Static Library格式的库多出一个文件夹,拥有一个独立的工作区
2.Framework格式的库中嵌入的依赖库可以和app环境的依赖库相同,并且不会产生duplicate symbol,因为Framework有独立的运行环境
3.Static Library格式的库中的依赖库和app环境依赖库相同时会有依赖冲突
4.Static Library格式的库的依赖的方法可以不实现,但Framework格式的库依赖的方法必须实现(objc)
5.Static Library格式的库不可包含图片等资源文件
6.Static Library格式的库的库在打包时会直接编译进app的二进制执行文件,Framework格式的库被打包进沙盒;如果观察过Product包内容时会发现使用<Static Library格式的库>或者<Framework格式的库且(mach -O Type = Static Library)>不会出现在Frameworks文件夹中,是因为编译进了app的二进制执行文件
7.开发者签名的Dynamic Library性质的库时在app extension中共享或者在macOS部分场景下共享;苹果官方的动态库有共享权限
8.制作三方库在考虑Static Library格式的库库或者Framework格式的库时:依赖图片资源,使用Framework格式的库;想要拥有完整的依赖关系,使用Framework格式的库,此时外界可能需要剔除Framework格式的库之外的依赖库,而采用Framework格式的库内的依赖库,否则虽然不会产生依赖冲突,但会增加包大小
Statlo Ubrary.png
1.选择Dynamic Library生成动态库,Static Library生成的是静态库。2.由于iOS8之后才允许使用插入动态库,所以网上大部分教程在选MachO-Type的时候选择的是Static Library。但是.a静态库和静态framework无法访问库中的资源图片的。所以自己创建的framework如果想访问库中的资源文件,那么必须要是动态库。3.自建动态库手动拖入的工程后,必须选择embed选项,否则的话,启动即崩溃。4.静态库中关联的对象无法用KVO进行观察,不会触动KVO回调方法。5.理解一下静态库和动态库点击地址进入
(4)设置.framework最低支持的版本
targ.png
不设置版本打出来的真机的framework只包含arm64架构,模拟器的framework只包含arm64和x86_64架构
(5)Link With Standard Libraries
Build Settings中Link下面的Link With Standard Libraries设置NO:避免重复链接
image.png
4.暴露头文件(.h)以供SDK使用者调用
TARGETS —> Build Phases -》 Headers
Sianing & Capabstes.png
5.在模拟器和真机中run四种模式
① debug 模式 模拟器运行
② debug 模式 真机运行
③ release 模式 模拟器运行
④ release 模式 真机运行
(1)X86_64 和 arm64 切换
使用模拟器运行,制作的SDK是X86_64
使用真机运行,制作的SDK是arm64
(2)编译配置
苹果架构分为x86_64(模拟器) arm64
苹果包分为 Debug 和 Release
一般都是使用arm64
调试时,使用Debug包,上线使用Release
(1)Debug 和 Release 切换
Release: 选中工程->Edit Scheme->Run->Build Configuration->Release
, dynamicFrameWork.png
Wan for the erecvtable to be launched.png
(2)Run工程获取SDK
Clean: Product -> Clean Build Folder
Run工程后,在Product->Show Bulid Folder in Finder -> Products 中获取SDK
dynamicFrameWork.framework.png
5.模拟器和真机通用架构模式合并
(1)查看debug真机和模拟器架构
lipo -info 路径
a.查看debug真机
Lost loain Wed Dec 174740 on tty$002.png
b.查看debug模拟器
ipo -info Users.png
(2)移除模拟器包含arm64架构
在合并模拟器和真机架构时,如果.a中都包含arm64架会导致真机和模拟器无法合并。报错:have the same architectures (arm64) and can't be in the same fat output file。
XCode12之前:编译模拟器静态库支持i386 x86_64两架构编译真机静态库支持armv7 arm64两架构使用lipo -create -output命令可以将两个库合并成一个支持模拟器和真机i386 x86_64 armv7 arm64四种架构的胖子库。
然而XCode12编译的模拟器静态库也支持了arm64,导致出现真机库和模拟器库不能合并的问题。
解决方案1:
如果有动态库工程:Build Settings -> Excluded Architectures里按照这样设置一下,再编译合并就不会报错了。
image.png
解决方案2:如果手里只有.framework文件
使用lipo remove命令将模拟器库的arm64架构移除
lipo 模拟器路径.framework -remove arm64 -output 模拟器路径.framework
MscBook-.png
(3)合并模拟器和真机架构
a.终端方式
sudo lipo -create [模拟器路径] [真机路径] -output [合成路径/文件名(自定义)]
合并成功如下:
圖 dynamicFrameWorkTest.png
b.脚本方式
(1)通过target创建 Aggregate
image.png
image.png
(2)在Dependencies中添加dynamicFrameWork
Se source Tags Buld Serthos Buld Pases Buss Rudes.png
ROJECT.png
(3)新建New Run Script Phase
Resource Tags Build Settings Bulld Phasos.png
(4).framework合并脚本
# Type a script or drag a script file from your workspace to insert its path.
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 "${DEVICE_DIR}" ] || [ ! -d "${SIMULATOR_DIR}" ]
then
exit 0
fi
# 如果合并包已经存在,则替换
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
# 使用lipo命令将其合并成一个通用framework
# 最后将生成的通用framework放置在工程根目录下新建的Products目录下
lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
#合并完成后打开目录
open "${SRCROOT}/Products"
fi
如下图:
T scriet or drag a ssript 5e ree Your workspace to.png
写好脚本后执行run,编译新 target,会自动在在Product->Show Bulid Folder in Finder -> Products 生成动态库。
6.使用动态库
(1)获取真机framework
在Product->Show Bulid Folder in Finder -> Products 中获取SDK,并拖入到新工程调用。
dynamicFrameWork.framework.png
a.如果你的framework合并了真机和模拟器会报错,如下:
Building for iOS, but the linked and embedded framework 'dynamicFrameWork.framework' was built for iOS + iOS Simulator.
解决方案:
将合并了模拟器和真机的framework剥离,只用真机的framework即可。如(1)图
b.注意事项
1.动态库的上架,只能上架真机版本,因为其结构不一样,是编译后的文件。因为苹果是不会让你上传模拟器版本到线上商店,这样会凭空增加app大小。
2.所以不需要合并真机和模拟器,选择真机架构framework拖入工程使用即可。如果动态库合并了真机和模拟器,编译会报错。(忽略步骤5.模拟器和真机通用架构模式合并)
3.静态库不同,静态库是会再通过编译,生成执行文件的。可以合并真机和模拟器。
提示:如果你非得用合并了的模拟器和真机的framework,你声明只能用#import "",不能使用#import <>。如下:
(不建议这么弄,不需要的直接忽略这条)
#import "dynamicFrameWork.framework/Headers/dynamicTestView.h"
#import "dynamicFrameWork.framework/Headers/testFrameWork.h"
(2)配置framework的Embed属性
Genera Signing & Copablnes RSOUrCe Teps Into Buld Settings Build Phases Buld Rules.png
(3)调用动态库中的文件
(a).dynamicTestView工程(.m)
image.png
(b).testFrameWork工程(.h/.m)
.h文件:
image.png
.m文件:
image.png
(c).开始调用动态库
image.png
可能遇到的问题
image.png
Reason:image not found
image.png