组件静态库化

一、基础了解

闭源库可以分为静态库和动态库
静态库 有.a 文件/.framework 文件 (白色的文件)
动态库 有.dylib/.framework 文件 (黑色的文件)

静态库支持的架构:(不同机型的CPU对应不同的架构)
模拟器:4s —— 5 : i386
5s – X : x86_64

真机:3gs – 4s : armv7
5/5c : armv7s(armv7兼容armv7s)
5s–X : arm64

系统默认选中那个模拟器便编译成对应架构的静态库。这个可不太方便。
解决办法:不止编译当前活跃架构,而是编译所有架构。
Build Settings -> Build Active Architecture Only ->No
但是现在老机型基本逐渐淘汰了,一般常用的就两个架构了:x86_64 arm64

查看静态库支持的架构:lipo -info 库文件

合成静态库:lipo -create 静态库文件A 静态库文件B -output xxxxx.a
分解静态库:lipo -remove i386 复合的静态库文件 -output xxxx.a
获取指定架构的静态库:lipo -thin arm64 复合的静态库文件 -output xxxx.a

查看framework支持的架构:lipo -info XXXX.framework/XXXX
查看framework的信息:file framework里的执行文件

二、制作步骤

(.a)静态库文件制作

1
2
3
4
5
6
7
8
9
1、创建点A文件项目 -Framework & library --> static library
2、把要编译成.a文件的源代码导入到项目
3、把.h文件暴露到public 里面(Build Phases/点+号,添加new headers phase文件/Headers/public)
4、选中EditScheme -> Run -> Info -> Build Configuration -> Release
5、选模拟器,然后command + b,再选iOS Device ,然后再command + b
6、打开项目的Products - 选中点a文件,右键show In Finder
7、使用终端lipo 命令,将真机和模拟器的.a文件进行合并
lipo -create Release-iphoneos/libMJExtension.a Release-iphonesimulator/libMJExtension.a -output libMJExtension.a
8、将.h文件和.a文件和.bundle文件存放在同一个文件下,就可以用了

(.framework)静态库文件制作

1
2
3
4
5
6
7
8
9
10
11
1、创建点framework文件项目 -Framework & library --> static framework
2、把要编译成二进制的源代码导入到项目
3、把.h文件暴露到public 里面(Build Phases/Headers/public)
4、选中EditScheme -> Run -> Info -> Build Configuration -> Release
5、设置build settings 的 mach-o Type:设置成 static Library
(制作不用考虑,但在静态framework 的使用时,如果有分类,还要进行设置 Other Linker Flags里的 : -ObjC )
6、选模拟器,然后command + b,再选iOS Device ,然后再command + b
7、打开项目的Products - 选中点a文件,右键show In Finder
8、使用终端lipo 命令,将真机和模拟器的.a文件进行合并
lipo -create Release-iphoneos/libMJExtension.a Release-iphonesimulator/libMJExtension.a -output libMJExtension.a
9、将.h文件和.a文件和.bundle文件存放在同一个文件下,就可以用了

framework注意事项:
默认使用xcode创建的framework是一个动态库(不能用,动态库不让上线)
测试动态库的时候,要在项目里配置:
Build Phrase - Copy File
Destination:frameworks
添加你导入到项目的框架

如果要使用xcode创建一个静态的framework:
设置build settings 的 mach-o Type:设置成 static Library
在静态的framework 使用时,如果有分类,还要进行设置 Other Linker Flags里的 : -ObjC

合成版的静态库方便使用,但是文件比较大,发布应用的时候可以使用真机版的减小体积。
一般提供给别人的都是模拟器发布版和真机发布版的静态库

framework制作规范:
1、把framework里面使用到的资源文件制作成一个.bundle文件
2、制作一个头文件,统一披露API
3、可以创建一个测试工程,再添加Target的方式制作framework,这样的复合工程,方便于边开发边调试。

三、怎样让组件的源码形式&二进制形式共存

在组件工程里,添加一个Target这种复合工程的形式来创建自己的framework,即可以方便调试和升级维护,也不用分开成两个仓库做维护。
对应调整的一些事项:
1、添加Target创建framework,代码与源码是引用关系。
2、设置framework生成的相关设置,把target设置成release模式,编译生成静态库。
2、在组件源码Classes文件夹同级目录里创建Products文件夹,并把编译好的静态库文件拷贝进来。
3、修改.podspec文件里的引用文件路径改为引用头文件和静态库
4、当宿主工程引用此组件的时候,更新不到最新的静态库,可以是因为缓存导致的问题,pod cache clean –all清除缓存和删除pod文件夹的内容从新下载资源。

四、组件的源码形式与二进制形式灵活切换

通过在pod install安装命令前面添加配置环境变量,来识别.podspec文件里的不同源路径,实现源码与二进制静态库的切换
示例:ProjectMain组件
ProjectMain.podspec文件里的source_files设置:

1
2
3
4
5
6
if ENV['IS_SOURCE'] || ENV['ProjectMain']
s.source_files = 'ProjectMain/Classes/**/*'
else
s.source_files = 'ProjectMain/Classes/**/*.h'
s.vendored_frameworks = 'ProjectMain/Products/ProjectMain.framework'
end

ENV[‘IS_SOURCE’]用于控制全局,即整个宿主工程的所有组件都统一设置了IS_SOURCE,全部组件就可以实现统一切换
ENV[‘ProjectMain’]用于控制单个组件,即只切换单一组件ProjectMain为源码,其它默认为二进制

pod install默认安装的所有组件为二进制库
IS_SOURCE=1 pod install安装的所有组件为源码
ProjectMain=1 pod install安装的ProjectMain组件为源码,其它组件为二进制

注意:二进制库无法安装可以尝试更新本地索引库:pod repo update
在二进制与源码版本切换时可能导致更新不了的问题,可是通过pod cache clean –all清除缓存和删除pod文件夹的内容从新下载来解决。

五、划分了子库的组件subspec

组件的源码分子库,打包静态库的时候不分组,统一打包成一个静态库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if ENV['IS_SOURCE']
s.subspec 'Base' do |b|
b.source_files = 'XMGFMBase/Classes/Base/**/*'
end
s.subspec 'Category' do |c|
c.source_files = 'XMGFMBase/Classes/Category/**/*'
end
s.subspec 'Network' do |n|
n.source_files = 'XMGFMBase/Classes/Network/**/*'
n.dependency 'AFNetworking'
end
s.subspec 'Tool' do |t|
t.source_files = 'XMGFMBase/Classes/Tool/**/*'
end
else
s.source_files = 'XMGFMBase/Classes/**/*.h'
s.vendored_frameworks = "XMGFMBase/Products/XMGFMBaseBinary.framework"
s.dependency 'AFNetworking'
end

如果子库依赖有其它第三方框架,打包成静态库的时候不应该包含进去。
组件测试工程里Podfile文件,引用静态库Targer工程ProjectBaseLib

1
2
3
4
5
6
7
8
9
10
11
12
target 'ProjectBase_Example' do
pod 'ProjectBase', :path => '../'
target 'ProjectBase_Tests' do
inherit! :search_paths
end
target 'ProjectBaseLib' do
end
end

打赏支持一下呗!