前语
咱们之前经过一篇文章在评论“移动客户端架构规划
的时分,仅是经过一篇文章进行了总述。
后来,我就换工作,来到了Nike。近半年一向很忙,最近接到一个工单,要求升级SDK。借此关键,简略回顾一下iOS开发包二进制化,并进一步认识一下咱们比较陌生的 XCFramework
一、总述
项目中库的区分:
- 咱们在实践开发中,若是项目比较巨大,咱们能够将 库 区分为:
私有库:
(若是咱们的公司移动开发建造比较成熟,也能够理解为咱们内部移动开发套件的私有库)
二方库
: 因项目建造需求而收购的,由 服务 供应商 供给的SDK库。如:埋点服务SDK、阿里的反爬虫服务SDK、OCR扫描服务等。(一般需求付费 或 有限 免费体会 SDK功用)
三方库
: 免费运用的外部库(一般来源于Github等开源渠道)iOS开发中的库的二进制化处理(功用模块闭源处理)
- 依据是否将库的功用服务完成代码开源,咱们又能够将库 区分为 “
开源库
”、“闭源库
”
二进制化
: 把功用服务模块的源码 打包成 【闭源库】的开发,也就是iOS开发包二进制化
处理!!!!(二进制化处理也能够称为SDK开发)
- 在iOS二进制化开发中,能够将模块打包成
静态库
/动态库
的方式
- 这些
静态库
、动态库
,能够统称为二进制库
;
- 二进制化库开发完毕后,咱们能够把库
集成
在一个项目里边进行运用;
1. 二进制化库的几种分类
在iOS开发中的二进制化库能够简略区分为,静态库、动态库(.framework)(注:不是一切的.framework就一定是动态库)
-
静态库: 以
.a
或许.framework
作为文件的扩展名。链接时完整地拷贝至可执行文件中,被屡次运用就有多份冗余拷贝 -
动态库: 以
.dylib
或许.framework
作为文件的扩展名。链接时不仿制,程序运转时由体系动态加载到内存,供程序调用,体系只加载一次,多个程序共用,节省内存。留意
:- 动态库只能苹果运用,假如项目中运用了动态库不允许上架(如:jspatch)
- 咱们在开发进程中,即便有用到体系动态库的需求,也要常常查看对动态库的引进需求,因为这会影响App的发动速度。(若是不太了解这一块的朋友,能够经过我的这篇文章简略了解一下:App发动装载的进程)
-
.a
与.framework
的差异? -
- .a是一个纯二进制文件,.framework中除了有二进制文件之外还有资源文件;
-
- .a文件不能直接运用,至少要有.h文件配合,.framework文件能够直接运用。
-
- .a + .h + sourceFile = .framework
-
- 主张用.framework
2. 简略了解CPU架构
咱们在开发好功用服务模块,预备导出二进制化包的时分,咱们要依据 本身投放应用市场的需求硬件适配需求,对库进行的CPU架构的支撑。从而在满意本身需求的一同,防止导出二进制化包的体积过大。
2.1 什么是CPU架构?
CPU架构是CPU厂商给归于同一系列的CPU产品定的一个标准,首要目的是为了区分不同类型CPU的重要标明。
现在市面上的CPU分类首要分有两大阵营,一类是复杂指令集(CISC)CPU,另一类是精简指令集(RISC)CPU。咱们今日要评论的ARM指令集就归于精简指令集(RISC)CPU。
2.2 ARM处理器
ARM处理器是英国Acorn有限公司规划的低功耗本钱的第一款RISC微处理器。全称为Advanced RISC Machine。
咱们之前有一篇文章是关于探究iOS底层原理|ARM64汇编的。其间简略介绍了,不同的型号真机硬件设备的CPU硬件架构:
架构 | 设备 |
---|---|
armv6 | iPhone, iPhone2, iPhone3G, 第一代、第二代 iPod Touch |
armv7 | iPhone3GS, iPhone4, iPhone4S,iPad, iPad2, iPad3(The New iPad), iPad mini, iPod Touch 3G, iPod Touch4 |
armv7s | iPhone5, iPhone5C, iPad4(iPad with Retina Display) |
arm64 | iPhone5S 以后 iPhoneX , iPad Air, iPad mini2以后 |
ARM处理器因其低功耗和尺寸小而闻名,iPhone的处理器全部都根据ARM。
咱们经常见到的armv7 | armv7s | arm64都是ARM处理器的指令集。
指令集应用于开发有如下特色:
- 一切指令集原则上向下兼容。
- Xcode的模拟器是运转在电脑上的,所以iPhone模拟器并没有运用ARM指令集(在Intel芯片的老款Mac上的iPhone模拟器),而是运用的X86(64为处理器)或许i386(32位处理器)。
- 经过Xcode打包时,会为支撑的一切的指令集编译出对应的指令集代码的数据包,所以工程支撑的指令集越多,生成的二进制包就越大。
2.3 进一步认识 指令集 对应的机型
机型-架构 对照表
架构 | 设备 |
---|---|
armv6 | iPhone, iPhone2, iPhone3G, 第一代、第二代 iPod Touch |
armv7 | iPhone3GS, iPhone4, iPhone4S,iPad, iPad2, iPad3(The New iPad), iPad mini, iPod Touch 3G, iPod Touch4 |
armv7s | iPhone5, iPhone5C, iPad4(iPad with Retina Display) |
arm64 | iPhone 5S、iPhone 6、iPhone 6 Plus、iPhone 6S、iPhone 6S Plus、iPhone 7、 iPhone 7 Plus、 iPad (2018)、 iPhone 8 、 iPhone 8 Plus 、 and iPhone X |
arm64e | iPhone XS 、iPhone XS Max 、 iPhoneXR、iPhone 11 、 iPhone 11 Pro、 iPhone 11 Pro Max、iPhone 12 、 iPhone 12 Mini 、 iPhone 12 Pro 、 iPhone 12 Pro Max … 等 |
模拟器
-
- 模拟器32位处理器测验需求i386架构
-
- 模拟器64位处理器测验需求x86_64架构
-
- 模拟器64位处理器测验需求arm64架构(
采用了苹果公司内部自行规划的M1系列芯片的新款Mac,该Mac上跑的模拟器架构也是arm64架构,但不是iPhone真机那种arm64
)
- 模拟器64位处理器测验需求arm64架构(
真机
-
- 真机32位处理器需求armv7,或许armv7s架构
-
- 真机64位处理器需求arm64架构
应对当时(2023年)市场常用的设备推荐适配 指令集
- 模拟器: 64位处理器
x86_64
架构、arm64
架构 - 真机:64位处理器
arm64
架构、armv7s
架构(iPhone5及其以上的设备)
2.4 二进制化库适配架构的设置Build Setting
Build Settings
中查找Architecture
,能够看到如下图的设置项:
-
Architectures
指定当时Target
被编译时支撑的指令集的列表,假如指定了多个架构,机会生成多个架构需求的二进制文件。 -
Build Active Architecture Only
指定是否只编译当时衔接设备所支撑的指令集。
默认Debug
时为YES
,Release
时为NO
。设置为YES
时,编译后只生成当时衔接设备所支撑的指令集代码,编译速度更快。 -
Excluded Architectures
指定当时Target
被编译时回绝接收的指令集列表,编译后终究生成的二进制文件支撑的指令集是Architectures
减去Excluded Architectures
。
二、开发二进制化库的实践
咱们前面有提及 静态库
、动态库
,的差异。咱们这儿就不再重复介绍。
咱们直接进入创立一个.framework静态库的实践(此次我采用的是Xcode14.1,与之前的Xcode版本的创立进程总体上大致相似,略有不同。若是同学们用比较老的Xcode版本,请自行寻找相应教程)
1. 首先创立.frmawork
项目
在framewrok
中能够封装入自己需求封装的内容
eg: 我在Logger.h
中加入了一个测验办法
//
// Logger.m
// NKTestOCFramework
//
// Created by Van Zhang on 2023/2/27.
//
#import "Logger.h"
@implementation Logger
+ (void)logWithString:(NSString *)log{
NSLog(@"Log:%@",log);
}
+ (NSString *)testString:(NSString *)string {
return [@"NKTestOCFramework: " stringByAppendingString:string];
}
@end
2. 接下来进行项目配置:
1、设置Build Setting参数 将Build Active Architecture only
设置为NO
2、设置Build Setting参数 Mach-O Type
为Static Library (配置静态、动态)
3、设置Build Setting参数 在Architectures
下增加 需求适配的硬件CPU,如 armv7s
、armv7
(咱们当下盛行的设备基本都是iPhone5s以上了,一般咱们直接arm64就够用了)
4、在Build Phases中设置需求公开和需求隐藏的头文件
5、将头文件引进到NKTestOCFramework
(自己SDK的头文件)
6、Command + B
运转项目,在Product
中找到framework
(在Xcode14创立的工程中,咱们找不到Product
文件夹,需求做如下调整:)
- 1、找到.xcodeproj文件
- 2、右键显示包内容
- 3、找到project.pbxproj并双击翻开
- 4、在文件内查找productRefGroup,周围还有一个mainGroup,把mainGroup后边的值仿制一下替换到productRefGroup后边,然后封闭文件而且保存。
3.framework的运用将封装好的.framework
拉入需求运用的项目中
4.工程运转
想要对Demo进行运转实践的朋友,能够经过以下链接,下载:
- github.com/VanZhang-CN…
三、二进制包的架构查看,包的兼并与拆分
1、相关概念以及惯例处理
- 二进制包文件都是 Mach-O 文件的一种(能够经过这篇文章简略了解Mach-O文件)
- iOS 的 二进制包 文件 分为
fat
二进制文件 和thin
二进制文件- 胖二进制文件:包括多套指令集架构的Mach-O文件(比方一同包括
armv7
、armv7s
、armv64
、i386
、x86_64
其间的两种以上) - thin二进制文件: 仅包括一套指令集架构的Mach-O文件
- 胖二进制文件:包括多套指令集架构的Mach-O文件(比方一同包括
- 二进制文件处理:
-
lipo -info
: 查看二进制文件支撑的架构
// 判别静态库所支撑的渠道 armv7 x86_64 arm64 lipo -info + 二进制文件途径
-
lipo -remove
: 移除二进制文件支撑的某架构
// 判别静态库所支撑的渠道 armv7 x86_64 arm64 lipo -remove + 要删去的指令集架构 + 二进制文件途径 + -output + 输出的二进制文件途径 如: lipo -remove armv7 origin_xxx.a -output op_xxx.a // 删去静态库包括的armv7渠道
-
lipo -thin
: 导出二进制文件支撑的某架构
// 判别静态库所支撑的渠道 armv7 x86_64 arm64 lipo -thin + 要导出的指令集架构 + 二进制文件途径 -output + 输出的二进制文件途径 如: lipo -thin arm64 origin_xxx.a -output op_xxx.a // 拆分静态库,只保存arm64 CPU架构
-
lipo -create
: 创立一个fat二进制文件使其支撑的多个架构
// 判别静态库所支撑的渠道 armv7 x86_64 arm64 lipo -create + 二进制文件途径1 + 二进制文件途径2 -output 输出的二进制文件途径 如: lipo -create device_xxx.a simulator_xxx.a -output universal_xxx.a //对真机或许模拟器分别打出 .a 文件兼并
-
2. 了解XCFramework
快速了解XCFramework:
官方视频链接:developer.apple.com/videos/play… 官方文档链接:developer.apple.com/documentati…
- XCFramework 是苹果新出的库类型,在 Xcode 11 及 cocoapods 1.9 以上版本被支撑
- 与普通动态库/静态库最大的差异是将多个渠道的二进制库,绑缚到一个可分发的
.xcframework
绑缚包中,支撑一切的苹果渠道和架构。- 这儿的关键词是多个渠道(iOS, macOS, tvOS, watchOS, iPadOS, carPlayOS)
- 咱们运用的普通动态库/静态库归于
fat file
,仅仅是包括多个架构,如armv7 armv7s arm64 arm64e x86_64
等 - XCFramework 能够包括 iOS 设备,iOS 模拟器和 Mac Catalyst 等多个渠道的二进制库。
- XCFramework库是把不同渠道、不同架构的Framework库放在一同(与Framework的开发相同,支撑C、OC、C++、Swift)
- 留意: 若是原本的Framework库中有C++文件(.hpp、.cpp),要尽或许地转换成Objective-C++文件(.h、.mm)。
- 咱们的项目若是存量项目(旧项目),一般开发项目的时分还是会或多或少地引进不同开发言语的库。
- 比方:Swift言语开发+Swift库+OC库
- OC言语开发+Swift库+OC库
- 只要咱们用到OC或OC库,内部若是运用了runtime 或 KVC、KVO等。无法防止的,咱们需求在项目主工程的Build Setting-> Other Linker Flag 增加一项:
-all_load
- 增加
-all_load
会导致 包括 了 C++ 的库,在导入 主工程 后 发生冲突 - 因此要尽或许 把 在 OC库中的C++文件(.hpp、.cpp)转换成Objective-C++文件(.h、.mm)
3. 终端指令制造XCFramework
经过xcodebuild -create-xcframework
命令即可完成制造 XCFramework
示例如下:
.
├── ios_arm64
│ ├── 0CD1FB8D-9D63-3092-B68B-2E579A306D3F.bcsymbolmap
│ ├── NKTestOCFramework.framework
│ └── NKTestOCFramework.framework.dSYM
└── ios_simulator_x86-64_arm64
├── NKTestOCFramework.framework
└── NKTestOCFramework.framework.dSYM
经过xcodebuild -create-xcframework
命令来兼并为 XCFramework。
xcodebuild -create-xcframework\
-framework ios_arm64/NKTestOCFramework.framework\
-framework ios_simulator_x86-64_arm64/NKTestOCFramework.framework\
-output NKTestOCFramework.xcframework
兼并后的 NKTestOCFramework.xcframework 目录结构如下,包括 arm64
和 x86_64
版本,这和 lipo 操作相似,兼并其他渠道时操作相似。
NKTestOCFramework.xcframework
├── Info.plist
├── ios-arm64
│ └── NKTestOCFramework.framework
└── ios-x86_64-simulator
└── NKTestOCFramework.framework
假如是静态库 .a 文件,则需求用-library
和-headers
来指定静态库和头文件。
xcodebuild -create-xcframework\
-library ios-arm64/libNKTestOCLib.a\
-headers ios-arm64/include/NKTestOCLib\
-library ios-simulator-arm64-x86_64/libNKTestOCLib.a\
-headers ios-simulator-arm64-x86_64/include/NKTestOCLib\
-output NKTestOCLib.xcframework
4.制造XCFramework的脚本
4.1 创立Framework脚本:模拟器形式
xcodebuild archive -project 'NKTestOCFramework.xcodeproj'\
-scheme 'NKTestOCFramework'\
-configuration Release\
-destination 'generic/platform=iOS Simulator'\
-archivePath './archives/NKTestOCFramework.framework-iphonesimulator.xcarchive'\
SKIP_INSTALL=NO
-
xcodebuild
: 在Xcode中实践运用的命令 -
archive
: 打包 -
project
: 工程名 -
scheme
: 选择 scheme -
configuration
: 哪种环境下 -
destination
: 要分发的渠道,当时指定的是 iOS Simulator -
archivePath
: 紧缩之后,存放的途径 -
SKIP_INSTALL=NO
:假如设置为YES,则不会将生成的framwork文件存放在Products目录下
4.2 创立Framework脚本:真机形式
xcodebuild archive -project 'NKTestOCFramework.xcodeproj'\
-scheme 'NKTestOCFramework'\
-configuration Release\
-destination 'generic/platform=iOS'\
-archivePath './archives/NKTestOCFramework.framework-iphoneos.xcarchive'\
SKIP_INSTALL=NO
4.3 创立Framework脚本
framework->XCFramework:
# 参数
scheme="<your scheme>"
framework_name=${scheme}
archive_path="archives"
archive_iphoneos_path="${archive_path}/iphoneos.xcarchive"
archive_iphone_simulator_path="${archive_path}/iphonesimulator.xcarchive"
out_dir="out"
# 清理
function build_clean() {
echo "======build_clean======"
rm -rf ./archives
rm -rf ./${out_dir}
}
# 编译
function build_framework {
echo "======build_framework======"
xcodebuild archive -scheme ${scheme} -sdk iphoneos -archivePath ${archive_iphoneos_path} BUILD_LIBRARY_FOR_DISTRIBUTION=YES SKIP_INSTALL=NO || exit 1
xcodebuild archive -scheme ${scheme} -sdk iphonesimulator -archivePath ${archive_iphone_simulator_path} BUILD_LIBRARY_FOR_DISTRIBUTION=YES SKIP_INSTALL=NO || exit 1
}
# 兼并
function build_xcframework() {
echo "======build_xcframework======"
xcodebuild -create-xcframework -framework "${archive_iphoneos_path}/Products/Library/Frameworks/${scheme}.framework" -framework "${archive_iphone_simulator_path}/Products/Library/Frameworks/${scheme}.framework" -output ${out_dir}/${framework_name}.xcframework || exit 1
}
# 紧缩
function build_zip {
echo "======build_zip======"
cd ${out_dir}
time=`date +%y%m%d%H%M%S`
name=${framework_name}_${time}.zip
zip -r -m -o ${framework_name}.xcframework.zip ${framework_name}.xcframework || exit 1
}
# 调用
build_clean
build_framework
build_xcframework
build_zip
留意修改参数值
-
scheme
=”要打包的scheme的具体值” -
archive_path
=”archives文件夹的途径” -
out_dir
=”输出文件夹的途径”
.a->XCFramework
# 参数
scheme="<your scheme>"
library_name=${scheme}
archive_path="archives"
archive_iphoneos_path="${archive_path}/iphoneos.xcarchive"
archive_iphone_simulator_path="${archive_path}/iphonesimulator.xcarchive"
# 静态库公共头文件目录
library_public_header_path="<your library public header path>" # 如:${scheme}/public_headers
out_dir="out"
# 清理
function build_clean() {
echo "======build_clean======"
rm -rf ./archives
rm -rf ./${out_dir}
}
# 编译
function build_library {
echo "======build_library======"
xcodebuild archive -scheme ${scheme} -sdk iphoneos -archivePath ${archive_iphoneos_path} BUILD_LIBRARY_FOR_DISTRIBUTION=YES SKIP_INSTALL=NO || exit 1
xcodebuild archive -scheme ${scheme} -sdk iphonesimulator -archivePath ${archive_iphone_simulator_path} BUILD_LIBRARY_FOR_DISTRIBUTION=YES SKIP_INSTALL=NO || exit 1
}
# 兼并
function build_xcframework() {
echo "======build_xcframework======"
xcodebuild -create-xcframework -library "${archive_iphoneos_path}/Products/usr/local/lib/lib${scheme}.a" -headers "${library_public_header_path}/" -library "${archive_iphone_simulator_path}/Products/usr/local/lib/lib${scheme}.a" -headers "${library_public_header_path}" -output ${out_dir}/${library_name}.xcframework || exit 1
}
# 紧缩
function build_zip {
echo "======build_zip======"
cd ${out_dir}
time=`date +%y%m%d%H%M%S`
name=${library_name}_${time}.zip
zip -r -m -o ${library_name}.xcframework.zip ${library_name}.xcframework || exit 1
}
# 调用
build_clean
build_library
build_xcframework
build_zip
专题系列文章
iOS架构规划
- 1.iOS架构规划|总述
- 2.iOS架构规划|iOS模块化开发(
待输出
) - 3.iOS架构规划|iOS组件化开发-依靠包管理工具Cocoapods常用实践:组件库(本地库、远程库(公有、私有))创立、模板工程、区分、资源管理、优化等
- 4.iOS架构规划|iOS组件化开发【组件管理、组件分类、组件区分、组件开发】(
待输出
) - 5.iOS架构规划|iOS开发包二进制化【.a静态库、.framework(静态库、动态库)、.dyld动态库、.xcfameworks等】
探究iOS底层原理
- 探究iOS底层原理|总述