objc系列译文(6.4):深入理解 CocoaPods

核心组件

CocoaPods是用ruby写的,并划分成了若干个Gem包。CocoaPods在解析执行过程中最重要的几个包的路径分别是:CocoaPods/CocoaPods、 CocoaPods/Core 和 CocoaPods/Xcodeproj。

CocoaPods / CocoaPod

这是面向用户的组件,每当你执行一个pod命令时,这个组件将被激活。它包括了所有实用CocoaPods的功能,并且还能调用其他gem包来执行任务。

CocoaPods / Core

Core gem提供了与CocoaPods相关的文件(主要是Podfile和podspecs)的处理。

Podfile

Podfile用于配置项目所需要的第三方库。它能被高度定制,所以你可以尽可能地给它添加你想要的特性。如果您还想对Podfile了解更多的话,请查看Podfile指南(地址 http://guides.cocoapods.org/syntax/podfile.html)。

Podspec

.podspec文件描述了一个库将怎样被添加进工程中。.podspec文件可以标识该第三方库所需要的源码文件、依赖库、编译选项,以及其他第三方库需要的配置。

CocoaPods / Xcodeproj

这个包负责工程文件直接关系的处理。它能创建以及修改.xcodeproj文件和.xcworkspace文件。它也可以作为一个独立的包使用,当你要编写修改项目文件的脚本时,可以考虑使用CocoaPods/Xcodeproj。

 

运行 pod install 命令

pod install的执行引发了很多操作。了解底层运行过程最简单的方式就是给pod install语句添加 –verbose 参数。现在,运行

1
pod install --verbose

将会出现以下执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
Analyzing dependencies
 
Updating spec repositories
Updating spec repo `master`
$ /usr/bin/git pull
Already up-to- date .
 
Finding Podfile changes
- AFNetworking
- HockeySDK
 
Resolving dependencies of `Podfile`
Resolving dependencies for target `Pods' (iOS 6.0)
- AFNetworking (= 1.2.1)
- SDWebImage (= 3.2)
- SDWebImage /Core
 
Comparing resolved specification to the sandbox manifest
- AFNetworking
- HockeySDK
 
Downloading dependencies
 
-> Using AFNetworking (1.2.1)
 
-> Using HockeySDK (3.0.0)
- Running pre install hooks
- HockeySDK
 
Generating Pods project
- Creating Pods project
- Adding source files to Pods project
- Adding frameworks to Pods project
- Adding libraries to Pods project
- Adding resources to Pods project
- Linking headers
- Installing libraries
- Installing target `Pods-AFNetworking` iOS 6.0
- Adding Build files
- Adding resource bundles to Pods project
- Generating public xcconfig file at `Pods /Pods-AFNetworking .xcconfig`
- Generating private xcconfig file at `Pods /Pods-AFNetworking-Private .xcconfig`
- Generating prefix header at `Pods /Pods-AFNetworking-prefix .pch`
- Generating dummy source file at `Pods /Pods-AFNetworking-dummy .m`
- Installing target `Pods-HockeySDK` iOS 6.0
- Adding Build files
- Adding resource bundles to Pods project
- Generating public xcconfig file at `Pods /Pods-HockeySDK .xcconfig`
- Generating private xcconfig file at `Pods /Pods-HockeySDK-Private .xcconfig`
 
  - Generating prefix header at `Pods /Pods-HockeySDK-prefix .pch`
 
- Generating dummy source file at `Pods /Pods-HockeySDK-dummy .m`
- Installing target `Pods` iOS 6.0
- Generating xcconfig file at `Pods /Pods .xcconfig`
- Generating target environment header at `Pods /Pods-environment .h`
- Generating copy resources script at `Pods /Pods-resources .sh`
- Generating acknowledgements at `Pods /Pods-acknowledgements .plist`
- Generating acknowledgements at `Pods /Pods-acknowledgements .markdown`
- Generating dummy source file at `Pods /Pods-dummy .m`
- Running post install hooks
- Writing Xcode project file to `Pods /Pods .xcodeproj`
- Writing Lockfile in `Podfile.lock`
- Writing Manifest in `Pods /Manifest .lock`
Integrating client project

整个过程中执行了很多操作,不过把它们分解之后,会发现它们都很简单。让我们逐步来分析。

阅读Podfile文件

你是否吐槽过Podfile的语法太过诡异,其实这是ruby的语法而不是OC。相较而言,Podfile要比现有的其他格式更加简单好用一些。

安装的第一步是要弄清楚哪些第三方库被显式或隐式地声明了。CocoaPods加载podspecs文件时,获取了第三方库的名称及版本列表。Podsspecs文件存储在本地,路径为~/.cocoapods。

版本控制和冲突

CocoaPods使用语义版本命名约定来解决对版本的依赖。由于冲突解决系统建立在非重大更改的补丁版本之间,这使得解决依赖关系要容易得多。举个栗子,两个完全不同的第三方库同时依赖CocoaLumberjack。它们其中一个依赖的版本是2.3.1,而另一个则为2.3.3,解析器可以自动使用较新的版本,在这里则是2.3.3,因为这可以与2.3.1向后兼容。

但这并不总是有效。有许多第三方库还并不支持这个约定,这让解决方案变得非常复杂。

当然,总是会有一些冲突需要手工解决。如果一个第三方库依赖CocoaLumberjack 1.2.5,而另一个依赖CocoaLumberjack 2.3.1,最后只能靠调用这两个第三方库的用户来手动地决定CocoaLumberjack的版本了。

加载源码

CocoaPods执行的下一个步骤是加载源代码。每个.podspec文件都包含了源代码的索引,这些索引一般指向了一个git地址或者git tag。它们以commit SHA码的方式存储在 ~/Library/Caches/CocoaPods中。而在这些路径中创建文件则由 Core 包负责。

源代码将依照Podfile、.podspec和缓存文件的信息下载到相应的第三方库路径。

生成Pods.xcodeproj

每次pod install 执行后并且检测到改动时,Pods.xcodeproj文件将呗Xcodeproj gem更新。如果Pods.xcodeproj文件不存在,则会以默认配置生成,若已存在,则Pods.xcodeproj会使用现有的配置。

安装第三方库

当Cocoapods向项目中增加了一个第三方库的时候,不仅仅是将添加代码这么简单。由于每个第三方库有不同的target,所以每次添加第三方库时,都只有几个文件被添加。每个源代码都需要:

  • 一个包含编译选项的.xcconfig文件

  • 一个同时拥有编译设置和CocoaPods默认配置的私有.xcconfig文件

  • 编译所必须的prefix.pch文件

  • 另一个编译必须的文件dummy.m

一旦每个pod的target都完成了以上步骤,整个Pods的Target就会被创建。这增加了相同的文件,与另外几个。如果有源代码中包含了资源bundle,向app的target中添加bundle的方式将写入Pods-Resources.sh。还有一个叫Pods-environment.h的文件,文件中含有许多检查组件是否来自pod的宏定义。最后,将生成两个确认文件,一个.plist文件,一个用于给用户查阅许可信息的markdown文件。

写入到磁盘

直到现在,许多已完成的过程都使用的是内存中的对象。为了让这些过程的结果可重复被使用,我们需要将所有结果都记录在一个文件中。所以Pods.xcodeproj和另外两个非常重要的文件:Podfile.lock和Manifest.lock都将被写入磁盘。

Podfile.lock

这是CocoaPods创建的最重要的文件之一。它记录了需要被安装的pod的每个已安装的版本。如果你想知道已安装的pod是哪个版本,可以查看这个文件。推荐将Podfile.lock文件加入到版本控制中,这有助于整个团队的一致性。

Manifest.lock

这是每次运行pod install时创建的Podfile.lock文件的副本。如果你见过“沙盒文件和Podfile.lock文件不同步”的错误,这个错误就是因Manifest.lock文件和Podfile.lock文件不一样引起。由于Pods所在的目录并不总在版本控制之下,这样可以保证开发者运行app之前都能更新他们的pods,否则app可能会crash,或者在一些不太明显的地方编译失败

xcproj

如果您已经依照我们的建议在系统上安装了xcproj,它会将您的Pods.xcodeproj文件转换成就旧有ASCII格式的plist文件。为什么要这么做呢?因为Xcode所依赖和使用的plist在很久以前就已经不被其他软件支持了。如果没有xcproj,你的Pods.xcodeproj文件将会以XML格式的plist文件存储,当你用Xcode打开它时,它会被改写,造成大量的文件冲突。

 

运行结果

运行pod install的最终结果是许多文件被添加到你的工程和系统中。这个过程通常只需要几秒钟。当然没有Cocoapods这些事也都可以完成。只不过所花的时间就不仅仅是几秒而已了。

 

旁注:持续集成

CocoaPods和持续集成在一起非常融洽。虽然持续集成很大程度上取决于你的项目配置,但Cocoapods依然能很容易地对项目进行编译。

Pods文件夹已加入版本控制的持续集成

如果包括Pods文件夹的一切东西都在版本控制之中,那么你不需要特别做什么就能够持续集成。由于编译时必须指定一个scheme,所以只需要保证使用了正确的scheme即可。

没有Pods文件夹的持续集成

如果你的Pods文件夹没有被纳入版本控制之中,那么你需要一些额外的步骤来保证持续集成的顺利进行。最起码,Podfile文件要放入版本控制之中。另外强烈建议将生成的.xcworkspace文件和Podfile.lock文件纳入版本控制,这样不仅简单方便,更能保证所使用的Pod是正确的版本。

一旦配置完毕,让CocoaPods在CI上正确运行的关键是保证每次编译之前都执行了pod install。在大多数系统中,譬如Jenkins或者Travis,你只用把“pod install”定义为一个编译步骤即可(实际上,Travis会自动执行pod install)。随着Xcode Bot的发布,我们还没有找到能像书写的这么流畅的解决方案,不过我们正朝着解决方案努力,一旦成功,我们将会立即分享。

 

结束语

CocoaPods简化了OC的开发流程,我们的目标是让第三方库更容易被发现和添加。了解CocoaPods的原理能让你做出更好的App。我们沿着CocoaPods的整个流程一步步执行,从载入specs文件和源代码、创建.xcodeproj文件和所有组件到将所有文件写入磁盘。所以接下来,我们运行 pod install –verbose,静静观察CocoaPods的魔力如何显现。


你可能感兴趣的:(CocoaPods)