iOS 二进制第三方库思路之Carthage编译静态库

											用Carthage编译你喜欢的静态库

在这里插入图片描述

前言

Carthage的基本用法已经在上一篇文章详细介绍了,本文主要针对一个问题的解决方案,那就是如何用Carthage编译第三方库为静态库。和pod package一样,封装了xcodebuild进行编译,省去了繁琐的参数设置,提供更便捷方法使用,Carthage作为后起之秀,维护力度和pod package相比,不是一个量级的,端午花了一天学习了下官方的文档,可以确定Carthage将会是第三方库打包成动态库或者静态库的首选。由于项目都还是Objective-C为主,因此我们不会全方位接入,而是使用它的打包功能,得力于他的松耦合,无侵入,可以配合Cocoapods结合使用,贼好用。

问题

Carthage默认只能编译成动态库。

解决方案

  • Carthage 0.29.0以下

staticFramework脚本起了一个临时文件xcconfig,写入如下信息进行静态库的编译参数设置

LD = the/path/to/ld.py
EDBUG_INFOMATION_FORMAT = dwarf

该方法是老版本制作静态库的方法, 但是在Xcode 9.4之后,原生已经支持了Swift源码的静态库,我们只需要修改对应的mach-o type即可。

  • Carthage 0.30.0之后
    当前版本以及到最新的版本,都已经支持了OCSwift的静态库,即使是已经支持了,但是如果你自己做一个库,要支持Carthage,默认的mach-o类型还是要是动态库类型,那么和之前的第三方库的制作和动态库基本没有任何区别。
  1. 库工程的project要支持shared schemes,Product > Schemes > Manage Schemes > Shared
  2. 使用的时候和动态库一样都需要手动拖入Build phase中的 Linked Binary,咱们等下用local pod 来做,都不需要手动拖

那么步骤如下:

  • 当我们check out对应的库源码后,找到对应project下面的target,Build SettingsLinking sectionMach-O Type改为Static Library
  • 执行carthage build --no-use-binaries --platfor ios,就可以在./Carthage/Build/$(PLATFORM_NAME)/Static下面生成对应的静态库
  • 默认的动态库我们需要添加脚本carthage copy-frameworks,咱们现在用的Static Framework不需要了,直接拖入即可

Why Static Frameworks

说了那么多,那么为什么我们这里要选用第三方库的静态库作为目标文件呢?
首先如果你注入太多动态库,App的启动时间就会变慢,也就是main函数之前动态链接的过程变久了。因此我们这里用Carthage来把动态库编译成静态库的方式解决这个问题。如果全部是静态库,那么都会被链接到App包里面,这样看起来包会增大,还有一个方案就是,把多个静态库通过一个动态库的工程包裹,包成一个动态库进行链接,这样静态库不会被打入包里面,而且也不会有太多的启动耗时。这里需要注意的是,也不是和无脑合并使用动态库,Apple给我们建议是最多6个动态库是最合适的传送门

关于静态库合并成动态库需要注意以下:
直接起一个动态库的project,然后把我们需要组合的静态库都拖入Link Binary with Libraries中,为了保证所有的静态库都会被合并,需要在Build SettingsOTHER_LDFLAGS中加入-all-load参数,强制链接所有静态库的全部内容,而不是有时候只链接合并其中一部分而已。

脚本自动编译静态库

根据上面的介绍,我们需要手动修改CarthageCheckouts中目标Target下对应的Mach-O Type类型为Static Library,这看上去没有问题,但是如果库更新后,我们的Checkouts源码又会被重置,这显然很不优雅,如果哪天忘记修改了,又变成动态库了,因此就有了如下脚本。

先介绍下脚本核心参数:

  • Mach-O Type
    Build Setting中的MACH_O_TYPE决定了目标文件被编译成哪种。
  1. executable 可执行文件,也就是我们平时的App架构
  2. dynamic library 动态库
  3. static framework 静态库 咱们现在要的就是这个 ✅ staticlib
  4. bundle 资源文件
  5. 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文件依赖即可,简直不好太轻松。

Demo演示

  • 1.创建工程
pod lib create CarthageProj
  • 2.创建Cartfile
vim Cartfile
# 写入如下两个库
# github "AFNetworking/AFNetworking" == 3.1.0
# github "Masonry/Masonry" == 0.6.3
  • 3.Check out
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文件编译静态库的,这里如果直接编译,那就是默认的动态库

  • 4.封装编译脚本
./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

  • 5.引入工程

A、不用cocoapods
我们需要手动引入,拖入项目中或者Link Binary那里,然后最重要的是你要手动生成一个xxx.xcconfig的文件,然后在project中的Configurations中比如DebugRelease中指定对应的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的话,组合起来用,是不是非常爽。

  • 最终目的
    在这里插入图片描述
    通过静态库的依赖,撇去了源码的依赖,可以看到我们对AFNetworkingMasonry已经没有任何文件的编译信息了,这就是二进制化的好处,也是我们后续要做的,优化项目编译打包时间。

结论

通过这个端午的研究,发现通过Carthage打包静态库是个可行的方案,至少打包第三方库是可行的。二进制方案的一个环节算是又有了一个新的思路。

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页