工程化(四)——组件二进制工程框架搭建

2023-11-20 13:12:48 浏览数 (2)

一、为什么使用CocoaPods来进行组件二进制

三方库的管理工具有很多,Cocoapods只是其中一种,除此之外还有Carthage,接下来我们就来分析一下二者。

使用Carthage引入的库,需要将Xcode工程文件(.xcodeproj)配置清楚(也就是说,该库必须要有一个.xcodeproj工程),Carthage会根据工程配置将其打包成xcframework(默认是动态库,默认是组件二进制),同时需要手动将frameworks拷贝到项目中(copy-frameworks)。如果需要静态库的话,可以通过控制工程文件的framework类型来实现。Carthage的每个阶段都可以手动控制,这要求开发者对工程化的步骤要有充分的理解。

Carthage对工程文件的编译是需要请求Scheme的,并且这个Scheme需要是共享的,不可以是用户私有的配置。

Carthage是使用Swift编写的;每个阶段的功能都很清晰,每一个流程细节都需要开发者去手动调用,只要开发者对整个制作流程有足够了解,就可以轻松调用到任何节点的API去进行组件二进制的制作。

而使用Cocoapods是属于傻瓜式的,只要开发者熟悉cocoapods内置的Podfile/Podspec内置的DSL,就能上手,开发者无需考虑太多细节的东西。使用Cocoapods引入的库都是按照Cocoapods标准制作的,在制作的时候需要重新生成工程配置文件、需要通过验证,因此使用Cocoapods库的成本是很小的。

Cocoapods是使用Ruby编写的,并不是每一个iOS开发者都对Ruby特别熟悉。但是Cocoapods的使用人群是非常多的。

另外说一点,将源码构建成二进制的一个好处就在于,可以节省编译时间、提升编译速度。因为源码要变成可执行文件,需要先由源代码编译单元生成一个.o,然后再由.o去生成可执行文件,而我通过构建组件二进制,就可以将这一块的编译时间给节省下来,这样一来,我整个编译速度就会有一个大幅度提升。

二、组件二进制架构简述

App依赖三方库的格式有两种选择:源码、二进制。

如果需要依赖三方库的二进制的话,那么二进制可以在三方库的接入方进行生成,也可以在三方库的开发制作方进行生成。

如果二进制是在三方库的接入方进行生成,那么有两个弊端:

  1. 生成二进制是需要时间的,这就会影响到接入方App的编译速度
  2. 如果一个App依赖的所有的三方库都是由该App的开发人员进行三方库二进制的生成,其风险远大于在三方库开发的时候就将二进制给生成好。

所以,三方库二进制的生成一定要由三方库的制作方进行

三方库的源码是存放在Git云端的,这些源码不可能一下载下来就可以直接编译的,而是需要按照Cocoapods的规则标准导入到项目中,生成对应的project之后才能编译。

我们将三方库源码从源码的Source中下载下来,按照Cocoapods的规则去生成工程配置文件,然后调用自动化将对应的工程编译成二进制,再将二进制推到专门用于保存二进制的source中

这样的话,作为App的开发人员,当我需要引入一个三方库的时候,想使用源码形式就可以在源码的source中引用,想使用二进制形式就可以在二进制的Source中引用。

需要注意的是,这里的二进制一定是多架构的,也就是说,我们生成的三方库二进制需要覆盖到该三方库所需要支持的所有架构(模拟器、真机,iOS、watchOS、macOS等)

多架构可以通过两种方式来进行控制:lipo、xcframeworks

我们可以在终端中输入如下指令来查看lipo命令的介绍:

代码语言:javascript复制
man lipo

可以看到,lipo可以拆分多架构(比如它可以将包含多架构的胖二进制文件拆分成单架构的二进制文件),也可以将多架构合并到一起。但我不推荐使用lipo,因为其使用是比较复杂的

我们这篇文章讲的组件二进制,主要是通过xcframework的形式进行多架构处理的。xcframework不是任何编译链接器所支持的编译产物的格式,而是由Xcode支持的一种格式。Xcode之所以要支持xcframework这种格式,就是为了帮助开发者摆脱lipo的。

如上图所示,就是AFNetwork这个三方库使用xcframework生成的二进制。可以看到在xcframework中支持了多种架构:iOS、macOS、tvOS、watchOS,针对每一种架构,它都生成了对应的一个.a文件:

然后根据当前编译的的架构类型,找到对应架构的二进制产物.a文件,然后将该.a文件加载进工程中。也就是说,我需要哪个架构下的二进制,我就找到并加载进来,而不是先把所有架构加载进来之后再进行拆分

需要特别说明的一点是,xcframgework不但支持.framework,还支持.a、.dylib,也就是说,xcframework支持所有的库类型

三、组件二进制制作工具工程搭建

通过上面的分析,我们可以得出这样一条思路:我们将三方库源码从源码的Source中下载下来,按照Cocoapods的规则去生成工程配置文件,然后调用自动化将对应的工程编译成二进制,再将二进制推到专门用于保存二进制的source中

这里调用自动化将对应的工程编译成二进制,再将二进制推到专门用于保存二进制的source中 ,这个功能肯定是要通过一个工具去完成,这个工具就是我们所要制作的。

1,调用如下指令,创建一个名为cocoapods-norman-bin的gem工程:

代码语言:javascript复制
bundle gem cocoapods-norman-bin

完成之后就可以在对应文件夹路径下看到有了一个名为cocoapods-norman-bin的工程:

然后将cocoapods-norman-bin工程使用VSCode打开:

2,配置version

对version.rb进行show in finder,然后直接调整物理文件夹结构:

上图的文件夹层次是最一开始自动生成的文件夹层次,我将其调整成如下的层次:

可以看到,调整了物理文件夹的层次结构之后,VSCode中展示的结构也对应跟着实时改变了:

然后修改一下version.rb文件:

moudle的作用是给三方库定义命名空间,命名空间的作用就是防止类名冲突

我在命名空间NormanBin中定义了一个VERSION变量,记录三方库的版本号。

然后修改cocoapods-norman-bin.gemspec中的require_relative和spec.version这两个字段,如下:

这里需要额外说明的一点是,我这里的配置方式都是最简单最基础的配置,如果你想要去学习一些更高阶的配置方式,可以去找一些成熟的Gem库,然后查看其配置。如果遇到不懂的配置,一定不要慌,去查看其他的三方库的配置,然后仿着写就可以了。

3,配置files

gemspec中的files和podspec中的source_files的作用是一样的,它指定了应该包含在库中的源代码文件的规则列表

仿照着cocoapods这个Gem,我们将其配置成如下:

代码语言:javascript复制
spec.files = Dir["lib/**/*.rb"]   %w{ README.md LICENSE.txt }

需要说明的是,%w中的w是word的意思,%w{}是定义一个字符串数组的意思,这个数组中的元素通过空格分割,并且这个字符串数组中的元素是不需要加引号的。

4,配置executables

上篇文章中我们讲到,Ruby三方库中暴露出来的Ruby文件都是存放在bin目录下,也就是说,bin目录下存放的是在终端环境中可以直接调用的Ruby文件。

我们打开Cocoapods这个Ruby三方库,看到其bin目录下有两个文件:

而在cocoapods.gemspec中声明了executables为%w{ pod sandbox-pod }

executables的作用就是声明直接在终端进行调用的Ruby文件,声明的文件会被加载到当前shell环境的搜索路径PATH中去

5,配置require_paths

require导入的时候从哪一个路径下开始查找

代码语言:javascript复制
spec.require_paths = ["lib"]

6,依赖cocoapods

由于我们需要基于cocoapods进行开发,所以还需要依赖cocoapods:

代码语言:javascript复制
spec.add_runtime_dependency 'cocoapods'

7,安装相关三方库

最终的Gemfile如下:

可以看到Gemfile中只有一个gemspec,这说明将cocoapods-norman-bin.gemspec中定义的三方库都导入进来了。

最终的cocoapods-norman-bin.gemspec如下:

最终的文件结构如下:

然后来到终端,执行bundle install,来安装需要的三方库:

8,创建插件文件

Cocoapods要求,其command必须要在lib文件夹下面创建一个名为cocoapods_plugin.rb的文件,cocoapods会将该文件中定义的命令加载进来

也就是说,我现在需要定义一个cocoapods可以直接使用的command,那么为了能够在终端上通过cocoapods调起这个command,就需要将这个命令command放到cocoapods_plugin.rb文件中。

9,使用VSCode调试工程

创建launch.json文件:

这里program配置的是"${workspaceRoot}/bin/norman_bin”,我们先来调试一下norman_bin。

我修改norman_bin文件,里面只打印一句话,然后点击运行,可以看到调试控制台输出了打印的这句话:

到这里,工程搭建就完全走通了。

以上。

0 人点赞