写在前面
在前文中,梳理了iOS 平台下的 动态库
、静态库
和 framework
之间的关系,可以用如下一句话概括:
-
库
就是一个二进制文件,有动态和静态之分 - framework 是
库
的一种打包方式
在实际开发场景中,我们使用 cocopods 这个工具进行代码组件化管理:
- 我们使用 cocopods 创建 pod 库进行代码组件开发,然后通过 podfile 在其它地方使用
- 使用 cocoapods-packager 工具将 pod 库打包成 framework 对外输出
本文从如下两个大的方面来介绍,cocopods 中的动态库、静态库:
本文使用的 cocopods 版本是 1.5.2,Xcode 版本是 Version 10.1 (10B61)。
➜ iOSFrameworkDemo git:(master) ✗ pod --version
1.5.2
1. 制作 pod 库 WBSDK
制作教程网上有很多,这里就不 step by step 来做了。
CocoaPods 私有仓库的创建(超详细)
这里,创建一个名为 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 中查看运行日志,某一次运行的截图如下:
上图中,我们可以看出 Build 的基本顺序入下:
- Project Pods 下面的 target
- Project WBSDK 下面的 target
现在,我们需要观察一下 Pods 这个 project,我们聚焦于 WBSKD.framework 上面。
我们发现,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
cocoapods的静态库和动态库
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
How do I use Cocoapods in an embedded framework?
Name Mangling
CocoaPods 都做了什么?