用Carthage编译你喜欢的静态库
Carthage
的基本用法已经在上一篇文章详细介绍了,本文主要针对一个问题的解决方案,那就是如何用Carthage
编译第三方库为静态库。和pod package
一样,封装了xcodebuild
进行编译,省去了繁琐的参数设置,提供更便捷方法使用,Carthage
作为后起之秀,维护力度和pod package
相比,不是一个量级的,端午花了一天学习了下官方的文档,可以确定Carthage
将会是第三方库打包成动态库或者静态库的首选。由于项目都还是Objective-C
为主,因此我们不会全方位接入,而是使用它的打包功能,得力于他的松耦合,无侵入,可以配合Cocoapods
结合使用,贼好用。
Carthage
默认只能编译成动态库。
staticFramework脚本起了一个临时文件xcconfig
,写入如下信息进行静态库的编译参数设置
LD = the/path/to/ld.py
EDBUG_INFOMATION_FORMAT = dwarf
该方法是老版本制作静态库的方法, 但是在Xcode 9.4之后,原生已经支持了Swift
源码的静态库,我们只需要修改对应的mach-o type
即可。
OC
和Swift
的静态库,即使是已经支持了,但是如果你自己做一个库,要支持Carthage
,默认的mach-o
类型还是要是动态库类型,那么和之前的第三方库的制作和动态库基本没有任何区别。Product > Schemes > Manage Schemes > Shared
Build phase
中的 Linked Binary
,咱们等下用local pod
来做,都不需要手动拖那么步骤如下:
check out
对应的库源码后,找到对应project下面的target,Build Settings
,Linking section
把Mach-O Type
改为Static Library
carthage build --no-use-binaries --platfor ios
,就可以在./Carthage/Build/$(PLATFORM_NAME)/Static
下面生成对应的静态库carthage copy-frameworks
,咱们现在用的Static Framework
不需要了,直接拖入即可说了那么多,那么为什么我们这里要选用第三方库的静态库作为目标文件呢?
首先如果你注入太多动态库,App的启动时间就会变慢,也就是main函数之前动态链接的过程变久了。因此我们这里用Carthage
来把动态库编译成静态库的方式解决这个问题。如果全部是静态库,那么都会被链接到App包里面,这样看起来包会增大,还有一个方案就是,把多个静态库通过一个动态库的工程包裹,包成一个动态库进行链接,这样静态库不会被打入包里面,而且也不会有太多的启动耗时。这里需要注意的是,也不是和无脑合并使用动态库,Apple给我们建议是最多6个动态库是最合适的传送门
关于静态库合并成动态库需要注意以下:
直接起一个动态库的project,然后把我们需要组合的静态库都拖入Link Binary with Libraries
中,为了保证所有的静态库都会被合并,需要在Build Settings
的OTHER_LDFLAGS
中加入-all-load
参数,强制链接所有静态库的全部内容,而不是有时候只链接合并其中一部分而已。
根据上面的介绍,我们需要手动修改Carthage
中Checkouts
中目标Target下对应的Mach-O Type
类型为Static Library
,这看上去没有问题,但是如果库更新后,我们的Checkouts
源码又会被重置,这显然很不优雅,如果哪天忘记修改了,又变成动态库了,因此就有了如下脚本。
先介绍下脚本核心参数:
Build Setting
中的MACH_O_TYPE
决定了目标文件被编译成哪种。executable
可执行文件,也就是我们平时的App架构dynamic library
动态库static framework
静态库 咱们现在要的就是这个 ✅ staticlib
bundle
资源文件relocatable object file
这种据我了解是 多个动态库或者静态库的合并Debug infomation format
要保证xcodebuild
不会生成对应的dYSM
调试文件,否则的话会编译失败。因为静态库本身已经包含了debug symbols
。当静态库被集成到executable
的app架构中时,App最终打包生成的可执行文件,也会包含dSYM
文件,该文件包含了本身App的符号以及静态库的符号,所以我们需要把DEBUG_INFORMATION_FORMAT
仅仅设置dwarf
即可
Framework search paths
Carthage
如果编译的是静态库,会额外生成一个/Static
的文件夹存放对应的静态库,比如/Carthage/Build/iOS/Static
。
其实正常情况下都不需要设置,所以可以认为该选项是可选的,主要是因为库之前有依赖关系,比如A依赖B这种,可能会发生编译失败的情况,因此主动设置下对应的path,也是问题不大的。我们应该不会遇到这个问题,等下我是直接用Cocoapods
来管理这些编译好的库文件,我们还是和之前一样,熟练的编写podspec
文件依赖即可,简直不好太轻松。
pod lib create CarthageProj
vim Cartfile
# 写入如下两个库
# github "AFNetworking/AFNetworking" == 3.1.0
# github "Masonry/Masonry" == 0.6.3
carthage update --no-use-binaries --platform iOS --no-build
*** Fetching Masonry
*** Checking out AFNetworking at "3.1.0"
*** Checking out Masonry at "v0.6.3"
该脚本和之前的不同,跟了三个参数,分别代表不用远程编译的预编译二进制,只用本地的源码编译,第二个参数代表架构,第三个代表不进行编译,只要把源码check out
即可,编译我们等下用自己的脚本进行编译,别忘了我们上边是需要用到临时的xcconfig
文件编译静态库的,这里如果直接编译,那就是默认的动态库
./build-static-carthage.sh -p ios -d AFNetworking Masonry
-p | --platform
必须指定ios
,macos
,watchos
,tvos
其中之一,而且只能指定单个
-d | --dependencies
需要编译的Framework数组,从Cartfile
文件中拿就行了
脚本输出日志如下:
Building static frameworks with Xcode temporary xconfig file:
/tmp/static.xcconfig.4GLgSU
With contents:
MACH_O_TYPE = staticlib
DEBUG_INFORMATION_FORMAT = dwarf
FRAMEWORK_SEARCH_PATHS = $(inherited) ./Carthage/Build/iOS/**
Building with command:
carthage build --no-use-binaries --platform ios AFNetworking Masonry
*** xcodebuild output can be found in /var/folders/wh/hhkfnl5d3_j9lsvyjrdcfnv80000gn/T/carthage-xcodebuild.Cpoz1c.log
*** Building scheme "AFNetworking iOS" in AFNetworking.xcworkspace
*** Building scheme "Masonry iOS" in Masonry.xcworkspace
当脚本编译完后,我们需要的静态库就出现在了/Cartahge/Build/iOS/Static
下
A、不用cocoapods
我们需要手动引入,拖入项目中或者Link Binary
那里,然后最重要的是你要手动生成一个xxx.xcconfig
的文件,然后在project中的Configurations
中比如Debug
和Release
中指定对应的xcconfig
文件名称,其内容如下
FRAMEWORK_SEARCH_PATH = $(PROJECT_DIR)/ ../Carthage/Build/iOS/Static
OTHER_LINKER_FLAGS = -all_load -ObjC
FRAMEWORK_SEARCH_PATH
指定了库文件的搜索路径
OTHER_LINKER_FLAGS
告诉编译器要把静态库完完全全的链接到可执行文件中来。-ObjC
代表库文件中包含了Objective-C extensions
B、使用cocoapods
这里为了Demo简单,我们用local pod
进行演示,效果和远程pod是一样的,只是远程需要lint以下。
新建一个文件夹Library
,把刚才生成的库都给导入到这里,然后对应的库下面写一个podspec
文件,该文件从哪来呢?正好,直接从Carthage/Checkouts
中的源码中有,只是他作用的依赖是源码,我们把它复制过去,修改成依赖vendored_frameworks
的方式,所有的静态库都这么操作
然后编写Podfile
文件
# Uncomment the next line to define a global platform for your project
platform :ios, '10.0'
target 'CarthageProj' do
pod 'AFNetworking', :path => 'Library/AFNetworking'
pod 'Masonry', :path => 'Library/Masonry'
target 'CarthageProjTests' do
inherit! :search_paths
# Pods for testing
end
end
执行 pod install
,打开CarthageProj.xcworkspace
可以看到,这几个xcconfig
文件是cocoapods
帮我们生成的,也就是和我们自己手动添加一样道理,只是这里的参数更多更全,原理是一样的,所以大家熟悉cocoapods
的话,组合起来用,是不是非常爽。
通过这个端午的研究,发现通过Carthage
打包静态库是个可行的方案,至少打包第三方库是可行的。二进制方案的一个环节算是又有了一个新的思路。