二、cocopods 中的动态库、静态库和framework
写在前面
在前文中,梳理了iOS 平台下的 动态库
、静态库
和 framework
之间的关系,可以用如下一句话概括:
-
库
就是一个二进制文件,有动态和静态之分 - framework 是
库
的一种打包方式
在实际开发场景中,我们使用 cocopods 这个工具进行代码组件化管理:
- 我们使用 cocopods 创建 pod 库进行代码组件开发,然后通过 podfile 在其它地方使用
- 使用 cocoapods-packager 工具将 pod 库打包成 framework 对外输出
本文从如下两个大的方面来介绍,cocopods 中的动态库、静态库:
image.png本文使用的 cocopods 版本是 1.5.2,Xcode 版本是 Version 10.1 (10B61)。
➜ iOSFrameworkDemo git:(master) ✗ pod --version
1.5.2
1. 制作 pod 库 WBSDK
制作教程网上有很多,这里就不 step by step 来做了。
这里,创建一个名为 WBSDK
的 pod 库来演示。
2. 使用 WBSDK
使用 pod lib create
工具提供的模板创建的仓库中,Example 工程的 podfile 文件如下:
use_frameworks!
platform :ios, '8.0'
target 'WBSDK_Example' do
pod 'WBSDK', :path => '../'
target 'WBSDK_Tests' do
inherit! :search_paths
end
end
我们把最外层的 Example 工程称之为壳工程,壳工程要使用 pod 依赖 WBSDK。
➜ WBSDK git:(master) ✗ tree -L 2
.
├── Example
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ ├── Tests
│ ├── WBSDK
│ ├── WBSDK.xcodeproj
│ └── WBSDK.xcworkspace
├── LICENSE
├── README.md
├── WBSDK
│ ├── Assets
│ └── Classes
├── WBSDK.podspec
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
13 directories, 6 files
壳工程使用 WBSDK 的时候,并不是直接以源码的方式引入使用,而是以二进制库的方使用的。
壳工程使用 WBSDK 的方式有两种:
- 静态库
- 动态库
这个通过 podfile 中的 use_frameworks!
控制。
2.1 以动态库的方式使用 WBSDK
首先,在 podfile 中写入 use_frameworks!
,执行 pod install
命令,运行 Example 工程。
在 Xcode 中查看运行日志,某一次运行的截图如下:
image.png上图中,我们可以看出 Build 的基本顺序入下:
- Project Pods 下面的 target
- Project WBSDK 下面的 target
现在,我们需要观察一下 Pods 这个 project,我们聚焦于 WBSKD.framework 上面。
image.png我们发现,WBSKD.framework 的 mach-o type
是以 dynamic 的方式打包。
我们可以查看 Example 运行后的 .app 文件结构,查看结构如下:
➜ WBSDK_Example.app tree -L 2
.
├── Base.lproj
│ ├── LaunchScreen.storyboardc
│ └── Main.storyboardc
├── Frameworks
│ └── WBSDK.framework
├── Info.plist
├── PkgInfo
├── WBSDK_Example
├── _CodeSignature
│ └── CodeResources
└── en.lproj
└── InfoPlist.strings
7 directories, 5 files
WBSDK.framework
独立于 WBSDK_Example 这个可执行文件存在,也验证了我们上面的说法。
2.2 以静态库的方式使用 WBSKD
我们去掉 podfile 中的 use_framework!
之后,再次执行 pod install,然后重新运行代码。
使用同样的方式来验证 WBSDK 的引入方式,我们发现,此时壳工程以静态库 .a
的形式来依赖 WBSDK。
验证方式与 2.1 相同,此处不赘述。
3. 使用 cocoapods-packager 工具打包 framework
3.1 安装 cocoapods-packager
cocoapods-packagerGitHub 地址。
官方给出了安装教程,安装完成之后,在终端输入 pod package
,查看这个工具的基本使用如下。
$ pod package NAME [SOURCE]
两个基本参数:
-
NAME
: 需要打包仓库名称,必填参数 -
SOURCE
: pod repo地址,可选参数,默认是官方 repo
3.2 相关的参数
这里,我们只看和打包输出framework类型相关的参数 ,我们这里结合源码来理解这些参数。
这里我们主要研究三个参数,以及默认不传参数的情形:
--embedded
--library
-dynamic
- 默认不传
在 package.rb
中,我将和打包类型相关的源码抠出来,分析一下。
['--embedded', 'Generate embedded frameworks.'],
['--library', 'Generate static libraries.'],
['--dynamic', 'Generate dynamic framework.'],
--library
和 --dynamic
从三个参数的名称和解释中,这两个是没有歧义的:
-
--library
,表示以.a
的形式输出静态库 -
--dynamic
,表示以.framework
的形式输出动态库,按照之前我们的分类标准,是Embedded Framework
,如下图所示
那么问题来了,--embedded
是个什么东东?用这个参数打出来的究竟是动态库还是静态库?
--embedded
我们查看 initialize(argv)
函数。
def initialize(argv)
@embedded = argv.flag?('embedded')
@library = argv.flag?('library')
@dynamic = argv.flag?('dynamic')
@package_type = if @embedded
:static_framework
elsif @dynamic
:dynamic_framework
elsif @library
:static_library
else
:static_framework
end
# ……
end
在 builder.rb
中,build()
函数声明如下:
def build(package_type)
case package_type
when :static_library
build_static_library
when :static_framework
build_static_framework
when :dynamic_framework
build_dynamic_framework
end
end
综上,我们发现使用 --embedded
参数,会以 .framework
的形式输出静态库
,按照之前我们的分类标准,也就是 Static Framework
,如下图所示:
从源码中也可以看出,在不带参数的默认情况下,输出的也是 Static Framework。
默认参数和 --embedded
的区别
我们通过上面源码发现,不带参数
和加上--embedded
参数结果都是打包静态framework
,它们有区别么???
换言之,我们能说默认参数是 --embedded
么?
答案是否定的,在目录打包输出的目录结构上还是有些许差异。
下面抠出了相关代码,这部分代码结合3.4一起看。
def framework_path
if @embedded
@spec.name + '.embeddedframework' + '/' + @spec.name + '.framework'
else
@spec.name + '.framework'
end
end
# package.rb
builder.build(@package_type)
return unless @embedded
builder.link_embedded_resources
# builder.rb
def link_embedded_resources
target_path = @fwk.root_path + Pathname.new('Resources')
target_path.mkdir unless target_path.exist?
Dir.glob(@fwk.resources_path.to_s + '/*').each do |resource|
resource = Pathname.new(resource).relative_path_from(target_path)
`ln -sf #{resource} #{target_path}`
end
end
总结
cocoapods-packager
通过三个参数来控制打包类型的:
-
--library
,以.a
的形式输出静态库 -
--dynamic
,以.framework
的形式输出动态库,也就是我们前文讲 的 Embedded Framework -
--embedded
,.framework
的形式输出静态库,对应我们前文讲 的 Static Framework - 默认不传参数,也会输出 Static Framework。
这里需要注意:
--embedded
和 Embedded Framework 的区别,不要混为一谈,二者有本质区别。
3.4 使用 cocoapods-packager 打包
为了测试方便,我们直接使用本地代码来打包,修改一下 WBSDK.podspec 中的Source 地址,指向本地仓库。
s.source = { :git => '/Users/xieshoutan/Code/Blog/pods/WBSDK' }
我们在 pod package
命令中加入 --force
参数,本次打包脚本强制执行,会覆盖上一次打包结果。
打包 Static Framework(默认不带参数)
使用默认 pod package
命令打包静态库:
➜ WBSDK git:(master) ✗ pod package WBSDK.podspec --force
打包输出的文件放在 WBSDK-0.1.0
文件夹下,查看输出结果:
├── WBSDK-0.1.0
│ ├── WBSDK.podspec
│ ├── build
│ │ ├── Pods.build
│ │ └── XCBuildData
│ └── ios
│ └── WBSDK.framework
我们可以验证 WBSDK.framework
是一个静态库。
build
文件夹下面是本次编译打包的日志。
打包 Static Framework(--embedded)
使用 pod package
命令加上--embedded
,打包静态库:
➜ WBSDK git:(master) ✗ pod package WBSDK.podspec --embedded --force
打包输出的文件放在 WBSDK-0.1.0
文件夹下:
├── WBSDK-0.1.0
│ ├── WBSDK.podspec
│ ├── build
│ │ ├── Pods.build
│ │ │ └── Release-iphonesimulator
│ │ └── XCBuildData
│ │ ├── BuildDescriptionCacheIndex-5324f43b9a88d4ed571fe98b8ba8ab33
│ │ ├── build.db
│ │ ├── f2a06309409592aa39514fee692f5f80-desc.xcbuild
│ │ └── f2a06309409592aa39514fee692f5f80-manifest.xcbuild
│ └── ios
│ └── WBSDK.embeddedframework
│ ├── Resources
│ └── WBSDK.framework
我们发现,和不带参数的静态库相比,这次打包结果文件目录结构发生了一些变化:
-
WBSDK.embeddedframework
对输出结果进行了一次封装 -
WBSDK.framework
同级目录多了一个 Resource 文件夹,如果在 .podsec 中有资源文件引入,Resource 文件夹下会有对应的 bundle 文件
上一节最后列出了与之相关的源码,可以结合查看,辅助理解。
打包 Embedded Framework(--dynamic)
使用 pod package
命令打包动态库,后面添加一个 --dynamic
参数。
➜ WBSDK git:(master) ✗ pod package WBSDK.podspec --dynamic --force
打包输出的文件的动态库放在 WBSDK-0.1.0
文件夹下:
├── WBSDK-0.1.0
│ ├── WBSDK.podspec
│ └── ios
│ ├── WBSDK.framework
│ └── WBSDK.framework.dSYM
4. 总结
至此,我们在本篇中,研究了和cocopods相关的动态库、静态库以及framework的基础知识。
但是对于“资源引用”、“pod库中动态库、静态库或者framework的引用”等知识没有涵盖。这一部分内容会在后面的文章中讲述。
参考资料
https://developer.apple.com/library/archive/technotes/tn2435/_index.html
Embedding Frameworks In An App
Pod Authors Guide to CocoaPods Frameworks
Link Binary with libraries VS Embed Frameworks
An Introduction to Creating and Distributing Embedded Frameworks in iOS