一、静态库
1.创建静态库工程
Xcode -> Create a new Xcode project -> iOS -> Static Library2.将代码导入工程 选择Create groups(逻辑路径)
3.配置一些设置
(1)Build Active Architecture Only
Build Active Architecture Only 设置为NO的意思是当前打包的.a支持所有的设备.否则打包时只能用当前版本的模拟器或真机运行.(2)Dead Code Stripping
Build Settings中Link下面的Dead Code Stripping设置为NO:先不进行死代码剥离。(3)设置.a最低支持的版本
3.暴露头文件(.h)以供SDK使用者调用
(1)Bulid Phases -> Copy Files -> + 将所有.h Copy进工程
(2.1)创建Headers
(2.2)Headers -> + 导入所有头文件
(2.3)将所有需要暴露的头文件拖到public
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
(2)Run工程获取SDK
Clean: Product -> Clean Build Folder
Run工程后,在Product->Show Bulid Folder in Finder -> Products 中获取SDK
通过Product找到位置:
静态库是以.a后缀结尾,动态库是以.framework 后缀结尾
使用真机运行可能出现的问题:这是由于真机的系统版本低于工程设置的最低版本
5.模拟器和真机通用架构模式合并
(1)查看debug真机和模拟器架构
lipo -info 路径
a.查看debug真机
b.查看debug模拟器
(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里按照这样设置一下,再编译合并就不会报错了。解决方案2:如果手里只有.a或.framework文件
使用lipo remove命令将模拟器库的arm64架构移除
lipo 模拟器路径.a -remove arm64 -output 模拟器路径.a
(3)合并模拟器和真机架构
a.终端方式
sudo lipo -create [模拟器路径] [真机路径] -output [合成路径/文件名(自定义)]
合并成功如下:
b.脚本方式
(1)通过target创建 Aggregate(2)新建New Run Script Phase
(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
如下图:
写好脚本后执行run,会自动在在Product->Show Bulid Folder in Finder -> Products 生成静态库。
6.使用静态库
(1)文件(.h、.m)中封装好方法以及合并好生成的.a静态库
(2)将.h文件和.a库拖到需要集成的项目中
(3)将.h文件和.a库加到Build Phases中
a.将frameWorkTest1加到Compile Sourceas中
B.生成的.a静态库加在Link Binary With Libaries
(4)调用静态库方法
可能遇到的问题
报错2 duplicate symbols for architecture arm64
解决方案:
2 duplicate symbols for architecture arm64 常见问题,是在导入第三方SDK的时候出现的。
究其原因一定是SDK中包含了工程中原本存在的某个类:全文搜索找到此类,无用的话将其删除,有用改变名字即可。
二、动态库
1.创建动态库工程
Xcode -> Create a new Xcode project -> iOS -> Framework2.将代码导入工程 选择Create groups(逻辑路径)
(1)本例创建动态库的名字为dynamicFrameWork,项目会自动生成一个名为dynamicFrameWork的头文件
(3)不在framework自动生成的dynamicFrameWork.h文件中声明头文件,在新工程使用时会报警告,如下图:
1.虽然编译没问题,但作为一个强迫症患者,还是在自动生成的.h文件中把需要用到的头文件全部导入。
2.使用时更加方便,只要#import 即可使用动态库中的包含的全部文件,更加节省代码。
大家可自定义自己的UI界面、功能或逻辑。代码写完后,可在路径TARGETS->Build Phases->Headers下将你需要暴露给外面调用的头文件添加到Public下面即可将文件拖入工程(步骤4中会说)
3.配置一些设置
(1)Build Active Architecture Only
Build Active Architecture Only 设置为NO的意思是当前打包的.a支持所有的设备.否则打包时只能用当前版本的模拟器或真机运行.(2)Dead Code Stripping
Build Settings中Link下面的Dead Code Stripping设置为NO:先不进行死代码剥离。(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包内容时会发现使用或者不会出现在Frameworks文件夹中,是因为编译进了app的二进制执行文件
7.开发者签名的Dynamic Library性质的库时在app extension中共享或者在macOS部分场景下共享;苹果官方的动态库有共享权限
8.制作三方库在考虑Static Library格式的库库或者Framework格式的库时:依赖图片资源,使用Framework格式的库;想要拥有完整的依赖关系,使用Framework格式的库,此时外界可能需要剔除Framework格式的库之外的依赖库,而采用Framework格式的库内的依赖库,否则虽然不会产生依赖冲突,但会增加包大小
1.选择Dynamic Library生成动态库,Static Library生成的是静态库。
2.由于iOS8之后才允许使用插入动态库,所以网上大部分教程在选MachO-Type的时候选择的是Static Library。但是.a静态库和静态framework无法访问库中的资源图片的。所以自己创建的framework如果想访问库中的资源文件,那么必须要是动态库。
3.自建动态库手动拖入的工程后,必须选择embed选项,否则的话,启动即崩溃。
4.静态库中关联的对象无法用KVO进行观察,不会触动KVO回调方法。
5.理解一下静态库和动态库
点击地址进入
(4)设置.framework最低支持的版本
不设置版本打出来的真机的framework只包含arm64架构,模拟器的framework只包含arm64和x86_64架构
(5)Link With Standard Libraries
Build Settings中Link下面的Link With Standard Libraries设置NO:避免重复链接4.暴露头文件(.h)以供SDK使用者调用
TARGETS —> Build Phases -》 Headers
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
(2)Run工程获取SDK
Clean: Product -> Clean Build Folder
Run工程后,在Product->Show Bulid Folder in Finder -> Products 中获取SDK
5.模拟器和真机通用架构模式合并
(1)查看debug真机和模拟器架构
lipo -info 路径
a.查看debug真机
b.查看debug模拟器
(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里按照这样设置一下,再编译合并就不会报错了。解决方案2:如果手里只有.framework文件
使用lipo remove命令将模拟器库的arm64架构移除
lipo 模拟器路径.framework -remove arm64 -output 模拟器路径.framework
(3)合并模拟器和真机架构
a.终端方式
sudo lipo -create [模拟器路径] [真机路径] -output [合成路径/文件名(自定义)]
合并成功如下:
b.脚本方式
(1)通过target创建 Aggregate
(2)在Dependencies中添加dynamicFrameWork
(3)新建New Run Script Phase
(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
如下图:
写好脚本后执行run,编译新 target,会自动在在Product->Show Bulid Folder in Finder -> Products 生成动态库。
6.使用动态库
(1)获取真机framework
在Product->Show Bulid Folder in Finder -> Products 中获取SDK,并拖入到新工程调用。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属性
(3)调用动态库中的文件
(a).dynamicTestView工程(.m)
(b).testFrameWork工程(.h/.m)
.h文件:(c).开始调用动态库
可能遇到的问题
Reason:image not found