深入理解CocoaPods

CocoaPods是开发OS X和iOS应用程序的一个第三方库的依赖管理工具。利用CocoaPods,可以定义自己的依赖关系(称作pods),并且随着时间的变化,以及在整个开发环境中对第三方库的版本管理非常方便。
CocoaPods背后的理念主要体现在两个方面。首先,在工程中引入第三方代码会涉及到许多内容。针对Objective-C初级开发者来说,工程文件的配置会让人很沮丧。在配置build phases和linker flags过程中,会引起许多人为因素的错误。CocoaPods简化了这一切,它能够自动配置编译选项。
其次,通过CocoaPods,可以很方便的查找到新的第三方库。当然,这并不是说你可以简单的将别人提供的库拿来拼凑成一个应用程序。它的真正作用是让你能够找到真正好用的库,以此来缩短我们的开发周期和提升软件的质量。
本文中,我们将通过分析pod安装(pod install)的过程,一步一步揭示CocoaPods背后的技术。

核心组件
CocoaPods是用Ruby写的,并由若干个Ruby包(gem)构成的。在解析整合过程中,最重要的几个gems分别是:CocoaPods/CocoaPods,CocoaPods/Core,和CocoaPods/Xcodeproj(是的。CocoaPods是一个依赖管理工具--利用依赖管理进行构建的!)。
CocoaPods是一个objc的依赖管理工具,而其本身是利用ruby的依赖管理gem进行构建的。

CocoaPods/CocoaPod
这是一个面向用户的组件,每当执行一个pod命令时,这个组件都将被激活。该组件包括了所有使用CocoaPods设计到的功能,并且还能通过调用所有其他的gems来执行任务。

CoocaPods/Core
Core组件提供支持与CocoaPods相关文件的处理,文件主要是Podfile和podspecs。

Podfile
Podfile是一个文件,用于定义项目所需要使用的第三方库。该文件支持高度定制,你可以根据个人喜好对其作出定制。

Podspec
.podspec也是一个文件,该文件描述了一个库是怎样被添加到工程中的,它支持的功能有:列出源文件、framework、编译选项和某个库所需要的依赖等。

CocoaPods/Xcodeproj
这个gem组件负责所有工程文件的整合。它能够对创建并修改.xcodeproj和.xcworkspace文件。它也可以作为单独的一个gem包使用。如果你想要写一个脚本来方便的修改工程文件,那么可以使用这个gem。

运行pod install命令
当运行pod install命令时会引发需要操作。要想深入了解这个命令执行的详细内容,可以在这个命令后面加上--verbose。现在运行这个命令pod install --verbose,可以看到类似如下的内容:
$ pod install --verbose

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语言写的。相较而言,这要比现有的其他格式更加简单好用一些。
在安装期间,第一步是要弄清楚显示或隐式的声明了哪些第三方库。在加载podspecs过程中,CocoaPods就建立了包括版本信息在内的所有的第三方库的列表。Podspecs被存储在本地路径~/.cocoapods中。

版本控制和冲突
Cocoapods使用语义版本控制-Semantic Versioning命名约定来解决对版本的依赖。由于冲突解决系统建立在非重大变更的不定版本之间,这使得解决依赖关系变得容易很多。例如,两个不同的pds依赖于CocoaLunberjack的两个版本,假设一个依赖2.3.4,另一个依赖2.3.3,此时冲突解决系统可以使用最新的版本2.3.3,因为这个可以向后与2.3.1兼容。

但这并不总是有效。有许多第三方库并不使用这样的约定,这让解决方案变得非常复杂。
当然, 总会有一些冲突需要手动解决。
如果一个库依赖于CocoaLumberjack的1.2.5,另外一个库则依赖于2.3.1,那么只有最终用户通过明确指定使用某个版本来解决冲突。

加载源文件
CocoaPods执行的下一步是加载源码。每个.podspec文件都包含一个源代码的索引,这些索引一般包括一个git地址和git tag。他们以commit SHAs的方式存储在~/Library/Caches/CocoaPods中。这个路径中文件的创建是由Core gem负责的。
Cocoapods将依照Podfile、.podspec和缓存文件的信息将源文件下载到Pods目录中。

生成Pods.xcodeproj
每次pod install执行,如果检测到改动时,cocoaPods会利用Xcodeproj gem组件对Pods.xcodeproj进行更新。如果该文件不存在,则用默认配置生成。否则,会将已有的配置项加载值内存中。

安装第三方库
当CocoaPods往工程中添加一个第三方库时,不仅仅是添加代码这么简单,还会添加很多内容。由于每个第三方库有不同的target,因此对于每个库,都会有几个文件需要添加,每个target都需要:
一个包含编译选项的.xcconfig文件
一个同时包含编译设置和CocoaPods默认配置的私有.xcconfig文件
一个编译所必须的prefix.pch文件
另一个编译必须的文件dummy.m
一旦每个pod的target完成了上面的内容,整个Pods target就会被创建。这增加了相同文件的同时,还增加了另外几个文件。如果源码中包含有资源bundle,将这个budle添加至程序target的指令将被添加到Pods-resources.sh文件中,还有一个名为Pods-environment.h的文件,文件包含了一些宏,这些宏可以用来检查某个组件是否来自pod。最后,将生成两个认可文件,一个是plist,另一个是markdown,这两个文件用于给最终用户查阅相关许可信息。

写入至磁盘
直到现在,许多工作都是在内存中进行的。为了让这些成果能被重复利用,我们需要将所有的结果保存到一个文件中。所以Pods.xcodeproj文件被写入此案,另外两个非常重要的文件:Podfile.lock和Manifest.lock都将被写入磁盘。

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

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

xcproj
如果你已经依照我们的建议在系统上安装了xcproj,它会对Pods.xcodeproj文件执行一下touch以将其转换成为旧的ASCII plist格式的文件。为什么要这么做呢?虽然在很久以前就不被其它软件支持了,但是Xcode仍然依赖于这种格式。如果没有xcproj,你的Pods.xcodeproj文件将会以XML格式的plist文件存储,当你用Xcode打开它时,它会被改写,并造成大量的文件改动。

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

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

Pods文件夹的版本控制
如果Pods文件夹和里面的所有内容都在版本控制之中,那么你不需要做什么特别的工作,就能够持续集成。如果我们只需要给.xcworkspace选择一个正确的scheme即可。

不受版本控制的Pods文件夹
如果你的Pods文件夹不受版本控制,那么你需要做一些额外的步骤来保证持续集成的顺利进行。最起码,Podfile文件要放入版本控制之中。另外强烈建议将生成的.xcworkspace和Podfle.lock文件纳入版本控制,这样不仅简单方便,也能保证所使用Pod的版本是正确的。
一旦配置完毕,在持续集成中运行CocoaPods的关键就是确保每次编译之前都执行了pod install命令。在大多数系统中,例如Jenkins或Travis,只需要定义一个编译步骤即可(实际上,Travis会自动执行pod install命令)。对于Xcode Bots,在书写这篇文章时我们还没能找到非常流畅的的方式,不过我们正朝着解决方案努力,一旦成功,我么将会立即分享。

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

原文章链接地址:https://objccn.io/issue-6-4/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容