注册

CocoaPod知识整理

前言

Pod库是很重要的组成部分,大部分第三方库都是通过CocoaPod的方式引入和管理,同时项目中的部分功能也可以用Pod库来做模块化。
本文是对CocoaPod的一些探究。
XS项目中的Pod库是很重要的组成部分,目前阅读器模块正在进行SDK化,需要用Pod库来管理,同时未来会做一些模块化的功能,同样需要用Pod库来处理。
本文对CocoaPods的一些内容进行探究。

正文

CocoaPods是为iOS工程提供第三方依赖库管理的工具,用CocoaPods可以更方便地管理第三方库:把依赖库统一放在Pods工程中,同时让主工程依赖Pods工程。Pods工程的target是libPods-targetName.a静态库,主工程会依赖这个.a静态库。 (下面会详细剖析这个处理过程)

CocoaPods相比手动引入framework或者子工程依赖的方式,有两个便捷之处:

所有Pod库集中管理,版本更新只需Podfile配置文件;
依赖关系的自动解析;
同时CocoaPods的使用流程很简单:(假设已经安装CocoaPods)
1、在xcodeproj所在目录下,新建Podfile文件;

2、描述依赖信息,以demo为例,有AFNetworking和SDWebImage两个第三方库:

target 'LearnPod' do
pod 'AFNetworking'
pod 'SDWebImage'
end

3、打开命令行,执行pod install ;

4、打开生成xcworkspace,就可以继续开发;

一、Podfile的写法

1、普通的写法;
pod 'AFNetworking' 或者 pod 'AFNetworking', '3.2.1',前者是下载最新版本,后者是下载指定版本。

2、指向本地的代码分支;

pod 'AFNetworking', :path => '/Users/loyinglin/Documents/Learn/AFNetworking'

指向的本地目录要带有podspec文件。

ac16fb5cafb076e4a5f34a2feb342ac7.png

3、指定远端的代码分支;

pod 'AFNetworking', :git => 'https://github.com/AFNetworking/AFNetworking.git', :branch => 'master'

指向的repo仓库要带有podspec文件。

20ee43c822d2d10b4079603977d8d515.png

4、针对特定的configurations用不同的依赖库

`pod 'AFNetworking', :configurations => ['Release']`

如上,只有Release的configurations生效;(同理,可以设置Debug)

7a981564ba406401fc2f30fd3e86a632.png

5、一些其他的feature

优化pod install速度,可以进行依赖打平:将pod库的依赖库明确的写在Podfile,主端已经提供对应的工具。

`require "bd_pod_extentions"`

`bytedanceAnalyzeSpeed(true)`

`bd_use_app('toutiao','thirdParty','public')`

post install的脚本,修改安装后的Pod库工程中的target设置;同理,可以修改其他属性的设置。

post_install do |installer_representation|
installer_representation.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ONLY_ACTIVE_ARCH'] = 'NO'`
end
end
end

类似的还有pre_install的脚本;(但是install之前可能都没有pods_project,所以用处也比较少;具体的参数意义可自查,以pods_project为例)

puts只有在添加--verbose参数可以看到,Pod::UI.puts则是全文可见。

pre_install do |installer|
puts "pre install hook"
Pod::UI.puts "pre install hook puts"
end

Podfile还可以设置一些警告提示的去除,第一行是去掉pod install时候的警告信息,第二行是去掉build时候的警告信息。

# 去掉pod install时候的警告信息
install! 'cocoapods', :warn_for_multiple_pod_sources => false
inhibit_all_warnings

二、Pods目录

Pods目录是pod install之后CocoaPod生成的目录。

8b8e507844e69eb8d3de2a3b9d71213b.png

目录的组成部分:

1、Pods.xcodeproj,Pods库的工程;每个Pod库会对应其中某个target,每个target都会打包出来一个.a文件;
2、依赖库的文件目录;以SDWebImage为例,会有个SDWebImage目录存放文件;
3、manifest.lock,Pods目录中的Pod库版本信息;每次pod install的时候会检查manifest.lock和Podfile.lock的版本是否一致,不一致的则会更新;
4、Target Support Files、Headers、Local Podspecs目录等;Target Support Files里面是一些target的工程设置xcconifg以及脚本等,Headers里面有Public和Private的头文件目录,Local Podspecs是存放从本地Pod库install时的podspec;

三、CocoaPods的其他重要部分

1.Podfile.lock文件

pod install会解析依赖并生成Podfile.lock文件;如果Podfile.lock存在时执行pod install,则不会修改已经install的pod库。(注意,pod update则会忽视Podfile.lock进行依赖解析,最后重新install所有的Pod库,生成新的Podfile.lock)
在多人开发的项目中,Pods目录由于体积较大,往往不会放在Git仓库中,Podfile.lock文件则建议添加到Git仓库。当其他人修改Podfile时,pod install生成新的Podfile.lock文件也会同步到Git。这样能保证拉下来的版本库是其他人一致的。

实际开发中,也会通过依赖打平来避免多人协作的Pod版本不一致问题。
pod install的时候,Pods目录下生成一个Manifest.lock文件,内容与.lock文件完全一致;在每次build工程的时候,会检查这两个文件是否一致。

28d704c99ee280be060cc809b6c42edd.png

2、Pod库的podspec文件
在每个Pod库的仓库中,都会有一个podspec文件,描述Pod库的版本、依赖等信息。
如下,是一个普通的Pod库的podspec:

ba27d35285ca593a282fd0cb0b1ced22.png

3、Pod库依赖解析

CocoaPod的依赖管理相对第三方库手动管理更加便捷。
在手动管理第三方库中,如果库A集成了库F,库B也集成了库F ,就会遇到库F符号冲突的问题,需要将库A/B和库F的代码分开,手动添加库F;后续如果库A/B版本有更新,也需要手动去处理。
而在CocoaPod依赖解析中,可以把每个Pod库都看成一个节点,Pod库的依赖是它的子节点; 依赖解析的过程,就是在一个有向图中找到一个拓扑序列。
一个合法的Podfile描述的应该是一个有向无环图,可以通过拓扑排序的方式,得到一个AOV网。
按照这个拓扑序列中的顶点次序,可以依次install所有的Pod库并且保证其依赖的库已经install。

有时候会陷入循环依赖的怪圈,就是因为在有向图中出现环,则无法通过算法得到一个拓扑排序。

四、Pods工程和主工程的关系

在实际的开发过程,容易知道Pods工程是先编译,编译完再执行主工程的编译;因为主工程的Linked Libraries里面有libPods-LearnPod.a的文件。(LearnPod是target的名字,下面的示例图都是用LearnPod作为target名)

57e30b66bdd4c8e83e35aa926a3222c7.png

那么Pod库中的target编译顺序是如何决定?
打开workspace,选择Pods工程。从上图分析我们知道,主工程最终需要的是libPods-LearnPod.a这一个静态库文件。
我们通常打包,最终名字都是target的名字;而静态库通常会在前面加上lib的前缀。所以libPods-LearnPod.a这个静态库的target名字应该是Pods-LearnPod。
从下图我们也可以确定,确实是在前面添加了lib的前缀。

00655a2ba44f53c26b39224483a853fc.png

看看Pods-LearnPod的Build Phases选项,从target依赖中可以看到其他两个target。

7763dffbc659bbbe70f0a87a22cdc901.png

分析至此,我们可以知道这里的编译顺序是AFNetworking、SDWebImage、Pods-LearnPod、LeanPod(主工程target)。
接下来我们分析编译过程。AFNetworking因为没有依赖,所以编译的时候只需要知道自己的.h/.m文件。

abe3fad679e72b54e9d14fdd7816df60.png

对于Pods-LearnPod,其有两个依赖,分别是AFNetworking和SDWebImage;所以在Header Search Paths中需要设置这两个库的Public头文件地址。

125e88d2950b06f1a54634d51781f376.png

编译的结果是3个.a文件(libPods-LearnPod.a、libAFNetworking.a、libSDWebImage.a),只有libPods-LearnPod.a是主工程的编译依赖。那么libPods-LearnPod.a是否为多个.a文件的集合?

93daac4ea3ee249b6894098be36bccf1.png

从libPods-LearnPod.a的大小,我们可以知道libPods-LearnPod不是多个.a的集合,仅仅是作为主工程的一个依赖,使得Pod库工程能先于主工程编译。
那么,主工程编译的时候如何去找到AFNetworking的头文件和.a文件?
从主工程的Search Paths我们可以看到,Header是有说明具体的位置;
同时Library也有相对应的Paths,在对应的位置放着libAFNetworking.a文件;

c49a1733cffe855f9803c960d1333862.png

这些信息是CocoaPod生成的一份xcconfig,里面的HEADER_SEARCH_PATHS和LIBRARY_SEARCH_PATHS会指明这两个地址。

0c4a51acd7d792cbf3be4389605485d1.png

对于资源文件,CocoaPods 提供了一个名为 Pods-resources.sh 的 bash 脚本,该脚本在每次项目编译的时候都会执行,将第三方库的各种资源文件复制到目标目录中。
CocoaPods 通过一个名为 Pods.xcconfig 的文件来在编译时设置所有的依赖和参数。
在编译之前会检查pod的版本是否发生变化(manifest和.lock文件对比),以及执行一些自定义的脚本。
Pod库的子target在指定armv7和arm64两个架构的时候,会分别编译生成armv7和arm64的.a文件;然后再进行一次合并操作,得到一个.a文件。
编译完成后进行链接,在armv7和arm64都指定时,会分别进行链接,最后合并得到可执行文件。
得到可执行文件后,会进行asset、storyboard等资源文件的处理;还会执行pod的脚本,把pod的资源复制过来。
全部准备就绪,就会生成符号表,包括.a文件里面的符号。
最后进行签名、校验,得到.app文件。

五、常用Pod指令

pod install,最常用的指令;
pod update,更新repo并重新解析依赖;
pod install --repo-update,类似pod update;
pod install --no-repo-update,忽略Pod库更新,直接用本地repo进行install;
pod update --no-repo-update,类似pod install;
pod update AFNetworking,更新指定库;

以上所有指令都可以添加 --verbose ,查看更详细的信息;
xcconfig在新增configuration之后,需要重新pod install,并修改xcconfig。

转自:https://www.jianshu.com/p/07ddbd829efc

0 个评论

要回复文章请先登录注册