iOS – 静态库、动态库从浅到深学习之路 (三)
XCFramework (framework的增强版)
说明:
1. 苹果官方推荐,支持的,可以更加方便多个平台和架构的分发二进制库的格式。
2. 需要xcode11以上支持
3. 在2019年提出的framework的另一种先进格式。
多架构合并
架构打包命令:
// 打包成模拟器架构
xcodebuild archive -project ‘SYTimer.xcodeproj’ \
-scheme ‘SYTimer’ \
-configuration Release \
-destination ‘generic/platform=iOS Simulator’ \
-archivePath ‘../archives/SYTimer.framework-iphonesimulator.xcarchive’ \
SKIP_INSTALL=NO
// 打包成iOS真机架构
xcodebuild archive -project ‘SYTimer.xcodeproj’ \
-scheme ‘SYTimer’ \
-configuration Release \
-destination ‘generic/platform=iOS’ \
-archivePath ‘../archives/SYTimer.framework-iphoneos.xcarchive’ \
SKIP_INSTALL=NO
胖二进制: 多个架构将库文件压缩放在一起,并不是合并。
通过lipo 命令进行将动态库进行合并。
格式 : lipo -output 合并后输出的名称 -create 架构路径1 架构路径2
lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer
遇到问题;
相同的架构合并,会出现冲突
解决方法:
去除相同的架构,只留一个架构,相关命令如下:
lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer
//提取 x86_64架构
lipo -output SYTimer-x86_64 -extract x86_64 ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer
lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer SYTimer-x86_64
苹果为了解决这个问题,所以引进了XCFramework ,优点相比于 lipo
1. 不用处理头文件
2. 会自动处理重复的架构
3. 会自动产生调试符号
4. 会根据运行的架构自动选择对应的架构 。
制作一个XCFramework:
xcodebuild -create-xcframework \
-framework ‘../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-framework ‘../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-output ‘SYTimer.xcframework’ 需要改xcframework放的路径
xcodebuild -create-xcframework \
-framework ‘../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-debug-symbols ‘架构1 – bcsymbolmap – 的*对路径’ \
-debug-symbols ‘架构2 – bcsymbolmap – 的*对路径’ \
-debug-symbols ‘架构1 – dsym – 的相对路径’ \
-framework ‘架构2 -framework – 的相对路径’ \
-debug-symbols ‘架构2 – dysm – 的相对路径’ \
-output ‘SYTimer.xcframework’
weak import
1. 弱引用的作用 ,就是当动态库被引入但没有使用的话,会自动置为nil ,不会让程序崩溃,编译通过,一个符号找不到,不会强制找到为止。 在xcconfig文件中格式为:
OTHER_LDFLAGS = $(inherited) -Xlinker -weak_framework -Xlinker “第三方库或动态库名称”
相同静态库的重复引入,符号冲突的解决方法:
通过链接器的 force_load 引入其中一个静态库, 另外一个静态库的符号通过 load_hidden 去隐藏。
命令如下:
OTHER_LDFLAGS = $(inherited) -l”*个静态库名” -l”第二个静态库名” -Xlinker -force_load -Xlinker “*个静态库路径.a” -Xlinker -load_hidden -Xliner “第二个静态库路径.a”
动态库 、静态库的实战
动态库与动态库的链接实战:
思路: 自己的framework,通过pod导入一个第三方,然后自己的项目,导入自己的framework并使用 ,反向使用等。
实现步骤:
1 .创建一个自己的framework (创建方法见 : framework制作)
2. 在framework,通过cocoapods导入一个第三方库。 (pods 只会生成链接器的参数,并不会把库文件放到framework下,pods是通过脚本是复制到指定目录,方式二:再使用的工程中,通过pods的配置文件,再指定加载的第三方库。)
3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。
4. 现在的流程就是 app -> 自己的动态库 -> 第三方动态库 (AFNetworking) , 大致的动态库与动态库的模型就出来了。
后期上传demo.
主要出现的问题:
1. 运行app,找不到第三方库文件(因为这个第三方库,是动态库中所引用的第三方库),报错信息:
Undefined symbols for architecture x86_64:
“_OBJC_CLASS_$_ZGRAppObject”, referenced from:
objc-class-ref in ZGRAFNetworkingManager.o
解决方法是:在pod 文件中为这个target也加入pod 第三方库的方法,Podfile 代码如下:
platform :ios, ‘14.1’
#自己做的framework ,动态库
target :’ZGRNetworkManager’ do
use_frameworks!
pod ‘AFNetworking’
end
#比喻App
target :’ZGRNetworkManagerTests’ do
use_frameworks!
pod ‘AFNetworking’
end
2. 找不到对应的符号_OBJC_CLASS_$_ZGRAppObject , 修改pod的配置文件,创建的target 和 动态库对应的配置文件都加上这句话。
OTHER_LDFLAGS = $(inherited) -framework “AFNetworking” -Xlinker -U -Xlinker _OBJC_CLASS_$_ZGRAppObject
动态库与静态库的链接实战:
思路: 自己的framework,通过pod导入一个第三方静态库 AFNetworking.a ,在framework 中添加一个target,模拟app来使用 。
实现步骤:
1 .创建一个自己的framework (创建方法见 : framework制作)
2. 在framework,通过cocoapods导入一个第三方库。 (pods 只会生成链接器的参数,并不会把库文件放到framework下,pods是通过脚本是复制到指定目录,方式二:再使用的工程中,通过pods的配置文件,再指定加载的第三方库。)
3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。
4. 现在的流程就是 app -> 自己的动态库 -> 第三方静态库 (AFNetworking.a) , 大致的动态库与静态库的模型就出来了。
注:
动态库链接静态库,会链接所有的静态库符号, 所以当app去编译动态库的时候,并不会报错。
静态库的导出符号,也会出现在动态库符号里面,所以可以在app中直接使用,导入头文件即可。 (搜索不到,需要设置 header search paths路径 ${SRCROOT}/Pods/Headers/Public)
后期上传demo.
关于静态库的符号隐藏,通过在pod的配置文件加入如下代码:
OTHER_LDFLAGS = $(inherited) -Xlinker -hidden-l “AFNetworking”
静态库与静态库的链接实战:
思路: 自己的framework静态库组件,通过pod导入一个第三方静态库 AFNetworking.a ,在framework 中添加一个target,模拟app来使用 。
实现步骤:
1 .创建一个自己的framework (静态库) (创建方法见 : framework制作)
2. 在framework,通过cocoapods导入一个第三方库静态库。
3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。
4. 现在的流程就是 app -> 自己的静态库组件 -> 第三方静态库 (AFNetworking.a) 。
操作记录:
1. 在静态库组件首先要设置 library search path , 例如 : “${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking”
2. 设置Other Linker Flags , -lAFNetworking
原理就是: app去链接静态库组件, 但静态库组件所链接的静态库并没有路径链接到app,所以需要在组件中手动设置下所链接的静态库的路径与头文件。
静态库与动态库的链接实战:
公式: app = app + 静态库 (所有的符号都是可见的)
思路: 自己的framework静态库,通过pod导入一个第三方动态库 AFNetworking ,在framework 中添加一个target,模拟app来使用 。
实现步骤:
1 .创建一个自己的framework ,静态库(创建方法见 : framework制作)
2. 在framework,通过cocoapods导入一个第三方库AFNetworking。
3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。
4. 现在的流程就是 app -> 自己的静态库 -> 第三方动态库 (AFNetworking) , 大致的静态库与动态库的模型就出来了。
Cocoapods 拷贝动态库脚本的用法。
首先,把脚本拷贝到工程根目录下,脚本文件,可以在任意cocoapods的工程中去找, 然后在项目中,在build phase 添加一个脚本,输入:
“${SRCROOT}/Pods-LGNetworkManagerTests-frameworks.sh”
这样在运行项目的时候,脚本会自动将动态库拷贝到product目录中。
总结:
1. XCFramework – sdk开发,组件开发
解决了:
1.头文件的处理
2. 调试符号的保存
3. 相同架构的处理
4.2019年才出。
2. 实战
1. weak_import
使用场景: 动态库 运行时 -》 不能确认到指定位置,所以用到了弱引用,当找不到动态库的时候,项目不会报错的作用
2. 静态库冲突 -》 app -> all_load\-Objc
3. app – 动态库 – 动态库链接 ,app不知道所以需要以下操作
方式一: 通过cocoapod脚本复制
方式二: 通过cocoapod 加载第三方库,并不会加载两次。 不用担心
动态库 – 静态库
存在问题:
静态库不想暴露到处符号,需要用到 -hidden -l静态库
app – 静态库 – 静态库
1. 名称 知道。
2 不知道所在位置: 所以只需要告诉位置
app – 静态库 – 动态库
1. 编译报错: 不知道动态库的路径
2. 运行报错: 不知道动态库的rpath