0. 简介
CocoaPods
是用Ruby
写的,并由若干个Ruby包(gems)
构成的。在解析整合过程中,最重要的几个gems
分别是:CocoaPods/CocoaPods, CocoaPods/Core, 和 CocoaPods/Xcodeproj 。
CocoaPods/CocoaPod
面向用户的组件,每当执行一个pod命令时,这个组件都会被激活。这个组件包括了所有使用CocoaPods涉及到的功能
,并且还能通过调用其它的gems来执行任务。CocoaPods/Core
Core 组件支持与 CocoaPods 相关文件的处理,文件主要是Podfile
和podspecs
。Podfile
Podfile 是一个文件,用于定义项目所需要使用的第三方库
。Podspec
.podspec
也是一个文件,该文件描述了一个库是怎样被添加到工程中的。它支持的功能有:列出源文件、framework、编译选项和某个库所需要的依赖等。CocoaPods/Xcodeproj
这个 gem 组件负责所有工程文件的整合。它能够对创建并修改.xcodeproj
和.xcworkspace
文件。它也可以作为单独的一个 gem 包使用。如果你想要写一个脚本来方便的修改工程文件,那么可以使用这个 gem。
1. 项目结构
1.1 新建一个不带测试模块的OC项目
目录结构如下:
├── HTDemo
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Assets.xcassets
│ ├── Base.lproj
│ ├── Info.plist
│ ├── SceneDelegate.h
│ ├── SceneDelegate.m
│ ├── ViewController.h
│ ├── ViewController.m
│ └── main.m
└── HTDemo.xcodeproj
├── project.pbxproj
├── project.xcworkspace
└── xcuserdata
1.2 新建Podfile模板
然后执行pod init
创建一个Podfile模板,在里面引入SDWebImage
和Masonry
,并且指定了Masonry
的版本号:
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'HTDemo' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for HTDemo
pod 'SDWebImage'
pod 'Masonry', '~> 1.1.0'
end
1.3 使用CocoaPods之后的项目结构
执行pod install
之后,就会将这两个库引入到项目中,此时项目目录如下:
├── HTDemo
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Assets.xcassets
│ ├── Base.lproj
│ ├── Info.plist
│ ├── SceneDelegate.h
│ ├── SceneDelegate.m
│ ├── ViewController.h
│ ├── ViewController.m
│ └── main.m
├── HTDemo.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ └── xcuserdata
├── HTDemo.xcworkspace
│ └── contents.xcworkspacedata
├── Podfile
├── Podfile.lock
└── Pods
├── Headers
├── Local\ Podspecs
├── Manifest.lock
├── Masonry
├── Pods.xcodeproj
├── SDWebImage
└── Target\ Support\ Files
除了使用pod init
创建的Podfile,其余的HTDemo.xcworkspace
、Podfile.lock
、Pods
都是由pod install
之后生成的。
2. CocoaPods安装的内容
2.1 .xcworkspace文件
xcworkspace
是一个项目容器,当有多个project需要相互依赖
时可以用xcworkspace
将它们组织起来。该文件下包含一个contents.xcworkspacedata
的文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:HTDemo.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
使用xml格式
将依赖包含在标签内。
cocoapods在首次安装三方库
时会生成一个叫Pods.xcodeproj
的project管理三方库,然后将该project
和主项目的project
通过workspace
进行管理。这样就可以在主工程里引入三方库了,而且三方库由Pods.xcodeproj
统一管理,不会对我们原项目产生任何干扰。
2.2 Podfile.lock
Podfile.lock
文件内容如下:
PODS:
- Masonry (1.1.0)
- SDWebImage (5.11.1):
- SDWebImage/Core (= 5.11.1)
- SDWebImage/Core (5.11.1)
DEPENDENCIES:
- Masonry (~> 1.1.0)
- SDWebImage
SPEC REPOS:
trunk:
- Masonry
- SDWebImage
SPEC CHECKSUMS:
Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
PODFILE CHECKSUM: 5891c4ea6e31c72930f38c52e6b5a23e24b702de
COCOAPODS: 1.10.1
因为Podfile文件里可以不指定版本号
,而版本信息又很重要,于是就有了Podfile.lock,它里面记录完整的版本信息和依赖关系
,下面会具体介绍。
2.2.1 PODS
PODS:
- Masonry (1.1.0)
- SDWebImage (5.11.1):
- SDWebImage/Core (= 5.11.1)
- SDWebImage/Core (5.11.1)
PODS中显示了引用的第三方库的具体版本号,pod是通过各个三方库的.podspec
文件找到对应依赖的。
2.2.2 DEPENDENCIES
DEPENDENCIES
为pod库的描述信息,这里内容是同Podfile
里的写法。因为我们指定了Masonry
的版本号,并没有指定SDWebImage
的版本号,所以这里内容也是一样的。
2.2.3 SPEC REPOS
SPEC REPOS:
trunk:
- Masonry
- SDWebImage
这里描述的是仓库信息,即安装了哪些三方库,他们来自于哪个仓库。
2.2.4 SPEC CHECKSUM
这里描述的是各个三方库的校验和
,校验和的算法是对当前安装版本的三方库的podspec文件求SHA1。podspec文件发生变化
意味着版本信息发生了变化,就需要重新同步代码。
2.2.5 COCOAPODS: 1.9.3
这个代表当前使用的CocoaPod版本号
,远程版本管理应该要保证大家使用的pod版本号一致。
2.3 Pods
2.3.1 Manifest.lock
Manifest.lock
是Podfile.lock
的副本,它是在Pods
目录里面。它的作用是这样的,我们通常是不把Pods文件放到版本管理里面
,而把Podfile.lock
放到版本管理里面。这时对于拉取代码之后是否需要更新pod,就可以通过对比本地的Manifest.lock和远程Podfile.lock是否相同
即可。
2.3.2 Targets Support Files
Pods安装的依赖是这样的组织形式:
这个Pods的Project下面有三个Targets,其中两个是依赖库,
Pods-HTDemo
是关联两个库的Framework。
2.3.2.1 Pods-HTDemo Framework
这个Framework,被用于工程项目的引用依赖,但是并不会打进包里(Do Not Embed),如下图所示:
这个工程下的配置文件:
- Framework文件这里还包含了用于管理Module的
modulemap
和umbrella.h
文件。modulemap
是对Module的声明文件,制作Framework我们总是需要该文件,它的内容如下:
framework module Pods_HTDemo {
umbrella header "Pods-HTDemo-umbrella.h"
export *
module * { export * }
}
其指向了一个umbrella
的头文件,这是制作Framework必须的头文件,modulemap
和umbrella.h
会在创建Module
时自动生成,不建议手动修改其关系。
- dummy.m文件
这是一个空的.m文件:
#import <Foundation/Foundation.h>
@interface PodsDummy_Pods_HTDemo : NSObject
@end
@implementation PodsDummy_Pods_HTDemo
@end
Xcode的编译是依赖.m文件的,如果一个库里没有.m文件,将不会被编译,为了防止这种情况就会在每个库里增加一个空的.m文件。有的时候在三方库的包里也会包含一个dummy文件。
dymmy意为假,说明是占位用的。
- xcconfig文件
xcconfig
文件是Build Setting配置项
的文件形式,它的优先级 > Xcode的Build Setting
,pod会自动生成debug
和release
两个环境下的xcconfig
文件,并且cocoapods会修改我们的工程配置,让这两个xcconfig文件生效:
来看一个pod生成的debug模式下的xcconfig文件,内容如下:
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -framework "Foundation" -framework "ImageIO" -framework "Masonry" -framework "SDWebImage" -framework "UIKit"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
如果修改里面的配置,结果会同步到工程target的Build Setting
配置中。
上面文件中配置FRAMEWORK_SEARCH_PATHS
和 HEADER_SEARCH_PATHS
用于帮助我们在项目中查找第三方库的HEADER_SEARCH_PATHS
和 HEADER_SEARCH_PATHS
,在工程target的Build Setting中可以看到,如下图所示:
一些自定义的参数,会体现到target的Build Setting
最后的User-Defined
中:
2.3.2.2 三方库 Framework
每个三方库也都有一些配置文件,文件格式基本一致,下图是Masonry的配置文件,Xcode中Pods > Pods > Target Support Files > Masonry
对应的文件就是该内容:
如果三方库也需要依赖别的库时,是如何找到依赖的这个库呢? 也是需要在Build Setting里进行配置,所以这也是Framework里xcconfig文件的作用。
3. Build Phases
这里是编译阶段配置的地方,当第一次pod install
成功之后,这里会多一些[CP]
(CocoaPods的缩写)开头的配置项,它们都是由CocoaPods添加的脚本内容,执行顺序,从上到下,如下图所示:
3.1 [CP]Check Pods Manifest.lock
可以看到,这里执行了一个脚本:对比Podfile.lock
和 Manifest.lock
文件(判断远端代码和本地依赖是否一致)如果不同,会提示让你重新pod install
。终于知道这个报错是在哪里写的了。
3.2 [CP] Embed Pods Frameworks
通过名字可以知道,这一步是嵌入第三方的Frameworks,里面会调用一个脚本文件:"${PODS_ROOT}/Target Support Files/Pods-HTDemo/Pods-HTDemo-frameworks.sh"
,同时也有个输入和输出文件,如下:
Pods-HTDemo-frameworks.sh
这个脚本文件主要的作用就是将三方库的frameworks导入到编译生成的的xxx.app/Frameworks
目录下。
4. pod install 详细内容
之前在使用pod install
命令的时候,从来没有关注过这个命令执行的详细内容。可以在这个命令后面加上--verbose
,来查看这个命令执行的详细信息:
# 分析依赖
Analyzing dependencies
# 检查目标CPU指令集
Inspecting targets to integrate
Using `ARCHS` setting to build architectures of target `Pods-HTDemo`: (``)
# 查找Podfile文件的改变
Finding Podfile changes
- Masonry
- SDWebImage
# 解析Podfile文件的依赖项
Resolving dependencies of `Podfile`
CDN: trunk Relative path: CocoaPods-version.yml exists! Returning local
because checking is only perfomed in repo update
CDN: trunk Relative path: all_pods_versions_1_1_7.txt exists! Returning local
because checking is only perfomed in repo update
CDN: trunk Relative path:
Specs/1/1/7/SDWebImage/5.11.1/SDWebImage.podspec.json exists! Returning local
because checking is only perfomed in repo update
CDN: trunk Relative path: all_pods_versions_a_a_4.txt exists! Returning local
because checking is only perfomed in repo update
CDN: trunk Relative path: Specs/a/a/4/Masonry/1.1.0/Masonry.podspec.json
exists! Returning local because checking is only perfomed in repo update
CDN: trunk Relative path:
Specs/1/1/7/SDWebImage/5.11.1/SDWebImage.podspec.json exists! Returning local
because checking is only perfomed in repo update
CDN: trunk Relative path:
Specs/1/1/7/SDWebImage/5.11.1/SDWebImage.podspec.json exists! Returning local
because checking is only perfomed in repo update
CDN: trunk Relative path: Specs/a/a/4/Masonry/1.1.0/Masonry.podspec.json
exists! Returning local because checking is only perfomed in repo update
# 将上面解析的规范与Manifest.lock文件进行比较
Comparing resolved specification to the sandbox manifest
- Masonry
- SDWebImage
# 下载依赖项
Downloading dependencies
-> Using Masonry (1.1.0)
-> Using SDWebImage (5.11.1)
- Running pre install hooks # 在执行install时,做了一些操作
# 生成Pods project
Generating Pods project
- Creating Pods project
- Installing files into Pods project
- Adding source files
- Adding frameworks
- Adding libraries
- Adding resources
- Linking headers
# 导入所有的Target
- Installing Pod Targets
- Installing target `Masonry` iOS 8.0
- Generating module map file at `Pods/Target Support
Files/Masonry/Masonry.modulemap`
- Generating umbrella header at `Pods/Target Support
Files/Masonry/Masonry-umbrella.h`
- Generating Info.plist file at `Pods/Target Support
Files/Masonry/Masonry-Info.plist`
- Generating dummy source at `Pods/Target Support
Files/Masonry/Masonry-dummy.m`
- Installing target `SDWebImage` iOS 9.0
- Generating module map file at `Pods/Target Support
Files/SDWebImage/SDWebImage.modulemap`
- Generating umbrella header at `Pods/Target Support
Files/SDWebImage/SDWebImage-umbrella.h`
- Generating Info.plist file at `Pods/Target Support
Files/SDWebImage/SDWebImage-Info.plist`
- Generating dummy source at `Pods/Target Support
Files/SDWebImage/SDWebImage-dummy.m`
- Installing Aggregate Targets
- Installing target `Pods-HTDemo` iOS 15.0
- Generating Info.plist file at `Pods/Target Support
Files/Pods-HTDemo/Pods-HTDemo-Info.plist`
- Generating module map file at `Pods/Target Support
Files/Pods-HTDemo/Pods-HTDemo.modulemap`
- Generating umbrella header at `Pods/Target Support
Files/Pods-HTDemo/Pods-HTDemo-umbrella.h`
- Generating dummy source at `Pods/Target Support
Files/Pods-HTDemo/Pods-HTDemo-dummy.m`
- Generating deterministic UUIDs
- Stabilizing target UUIDs
- Running post install hooks
- Writing Xcode project file to `Pods/Pods.xcodeproj`
Cleaning up sandbox directory
Integrating client project
Integrating target `Pods-HTDemo` (`HTDemo.xcodeproj` project)
- Running post integrate hooks
- Writing Lockfile in `Podfile.lock`
- Writing Manifest in `Pods/Manifest.lock`
CDN: trunk Relative path: CocoaPods-version.yml exists! Returning local
because checking is only perfomed in repo update
-> Pod installation complete! There are 2 dependencies from the Podfile and 2 total pods installed.
主要有以下几步:
读取podfile文件,解析依赖项
cocoapods会分析podfile中声明的依赖项
,并从本地索引库specs
中查找对应的三方库的Masonry.podspec.json
文件(如果要求的版本在本地索引库查不到,就会报错提示你需要先进行pod repo update
)和Manifest.lock文件做对比
检查要导入的三方库版本和Manifest.lock
中的版本是否一致加载源文件
cocoapods会按照xxx.podspec.json
和缓存文件的信息,将第三方库的源文件下载到Pods目录中生成 Pods.xcodeproj
每次pod install
执行,如果检测到改动时,CocoaPods 会利用Xcodeproj gem
组件对Pods.xcodeproj
进行更新。如果该文件不存在,则用默认配置生成。否则,会将已有的配置项加载至内存中。导入第三方库
当 CocoaPods 往工程中添加一个第三方库时,不仅仅是添加代码这么简单,还会添加很多内容。由于每个第三方库有不同的 target,因此对于每个库,都会有几个文件需要添加,每个 target 都需要:
a.一个包含编译选项的 .xcconfig 文件
b.一个同时包含编译设置和 CocoaPods 默认配置的私有 .xcconfig 文件
c.一个编译所必须的 prefix.pch 文件
d.另一个编译必须的文件 dummy.m
一旦每个 pod 的 target 完成了上面的内容,整个 Pods target 就会被创建。这增加了相同文件的同时,还增加了另外几个文件。如果源码中包含有资源 bundle,将这个 bundle 添加至程序 target 的指令将被添加到 Pods-Resources.sh
文件中。还有一个名为 Pods-environment.h
的文件,文件中包含了一些宏,这些宏可以用来检查某个组件是否来自 pod。最后,将生成两个许可文件,一个是 plist,另一个是 markdown,这两个文件用于给最终用户查阅相关许可信息。
- 写入到磁盘
直到现在,许多工作都是在内存中进行的。为了让这些成果能被重复利用,我们需要将所有的结果保存到一个文件中。所以Pods.xcodeproj
文件被写入磁盘,另外两个非常重要的文件:Podfile.lock
和Manifest.lock
都将被写入磁盘。