cocoapods

库的概念

库是共享程序代码的方式。库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。在开发过程中,一些核心技术或者常用框架,出于安全性和稳定性的考虑,不想被外界知道,所以会把核心代码打包成库,只暴露出头文件以供使用。库分静态库和动态库两种。

静态库

静态库,在iOS中会被打包成.a文件,配合.h头文件一起可以完成功能的调用。但是在在概念上,静态库是一种All In One的设计思路,因为依赖静态库的代码会把静态库完全链接到App的可执行文件中(mach-o)。也就是说,静态库是在编译器被链接到App中的,因此如果多个App都引用了同一个静态库,则每个App都会把这个静态库链接一份。静态库会导致mach-o文件过大,而mach-o文件直接影响app的启动时间和执行时占用的内存大小
当然,静态库的缺点不止于此。在使用静态库时,必须手动一个个链接它依赖的外部库,例如早期微信支付SDK的静态库接入方法中,必须要手动链接上:
SystemConfiguration.framework,
libz.dylib,
libsqlite3.0.dylib,
libc++.dylib,
Security.framework,
CoreTelephony.framework,
CFNetwork.framework
而且,静态库的特点导致了App每次启动时都要重新加载静态库的内存,无法控制加载时机,而且每次启动都需要重新加载静态库,导致二次加载时间无法被优化。
大部分时候,还需要在Other Linker Flags里填入Objc -all_load来确保静态库正常工作。
静态库链接

动态库

动态库,大部分会被打包成.tbd文件或者.dylib文件。不同于静态库在编译期链接到App,动态库是在运行时链接到App的,因此它有了三个好处:

按需加载,什么时候需要运行什么时候加载,提高了启动app的效率
因为存在多个app使用同一个动态库的情况,因此一旦某个动态库被加载到内存中,下一个app使用时无需再次耗费内存加载此动态库,大家公用一个动态库。
因为动态库不需要参与编译过程,因此不会产生链接时符号冲突的问题。

不过,苹果对动态库的完全支持仅停留在系统的动态库上,例如UI.framework,对于第三方的动态库,还是需要embed到系统中。早期的一些热更新框架,例如JSPatch钻了漏子通过dlopen来进行热更新,不过很快被禁掉了。
不过,如果是企业证书,还是可以在自己的app里灵活的加载第三方动态库的。

Framework

在解释静态库和动态库的过程中,我并没有提framework的字眼。有些开发者觉得framework文件就是动态库,其实并不准确。
我们提到的framework,指的是.framework文件,这既不一定是静态库,也不一定是动态库。实际上这是一种打包方式,将Header(头文件)、Binary(二进制代码文件)和bundle(资源文件)一起打包,方便开发者进行接入和调用。
因此framework到底是静态库还是动态库,取决于Binary文件(Mach-O文件)到底是静态库还是动态库。

注意

iOS平台 在 iOS8 之前,苹果不允许第三方框架使用动态方式加载,从 iOS8 开始允许开发者有条件地创建和使用动态框架,这种框架叫做 Cocoa Touch Framework。虽然同样是动态框架,但是和系统 framework 不同,app 中使用 Cocoa Touch Framework 制作的动态库 在打包和提交 app 时会被放到 app main bundle 的根目录 中,运行在沙盒里,而不是系统中。也就是说,不同的 app 就算使用了同样的 framework,但还是会有多份的框架被分别签名,打包和加载。不过 iOS8 上开放了 App Extension 功能,可以为一个应用创建插件,这样主app和插件之间共享动态库还是可行的。
苹果系统专属的framework 是共享的(如UIKit), 但是我们自己使用 Cocoa Touch Framework 制作的动态库是放到 app bundle 中,运行在沙盒中的

CocoaPods

简介

CocoaPods是iOS平台当前最流行的包管理工具,可以将它理解为一个可以自动部署到项目的组件池,而对应的podfile文件就相当于请求组件的Request。当组件下载到工程后,cocoaPods会自动完成组件集成到现有项目的工作,所有的依赖库都放到另一个名为Pods的项目中,然后让主项目依赖Pods项目,这样,源码管理工作都从主项目移到了Pods项目中。Pods项目最终会编译成一个名为libPods_name.a或者Pods_name.framework的文件,主项目只需要依赖这个文件即可。

使用pod install命令前
图片[1]-cocoapods-一一网
使用pod install命令后,多了一个pod项目,Podfile.lock文件,.以当前工程名字命名的.xcworkspace

在CocoaPods中,会存在以下几种文件:
.podspec

Pod的描述文件,一般来说表征你的项目地址,项目使用的平台和版本等信息

.podfile

用户编写的对于期望加载的pod以及对应Target信息

.podfile.lock

记录了之前pod加载时的一些信息,包括版本、依赖、CocoaPods版本等

.mainfest.lock

记录了本地pod的基本信息,实际上是podfile.lock的拷贝

pod install 运行原理分析

当我们运行pod install时,会发生:

  • 分析Dependency。

  • 对比本地pod的version和podfile.lock中的pod version,如果不一致会提示存在风险

  • 对比podfile是否发生了变化。

  • 如果存在问题,会生成两个列表,一个是需要Add的Pod(s),一个是需要Remove的Pod(s)。

  • (如果存在remove的)删除需要Remove的Pods

  • 添加需要的Pod(s)。

  • 此时,如果是常规的CocoaPods库(如果基于Git),会先去:

    • Spec下查找对应的Pod文件夹
    • 找到对应的tag
    • 定位其Podspec文件
    • git clone下来对应的文件(根据具体协议的不同,这里还可能存在以下几种方式的 download: Bazaar、Mercurial、HTTP、SCP、SVN)
    • copy到Pod文件夹中
    • 运行pre-Install hook
  • 生成Pod Project

    • 将该Pod中对应文件添加到工程中
    • 添加对应的framework、.a库、bundle等
    • 链接头文件(link headers),生成Target
    • 运行 post-install hook
  • 生成podfile.lock,之后生成此文件的副本,将其放到Pod文件夹内,命名为manifest.lock

(如果出现 The sandbox is not sync with the podfile.lock这种错误,则表示manifest.lock和podfile.lock文件不一致),此时一般需要重新运行pod install命令。

  • 配置原有的project文件(add build phase)

    • 添加了 Embed Pods Frameworks
    • 添加了 Copy Pods Resources
    • 添加了 Check Pods Manifest.lock

其中,pre-install hook和post-install hook可以理解成回调函数,是在podfile里对于install之前或者之后(生成工程但是还没写入磁盘)可以执行的逻辑,逻辑为:

pre_install do |installer| 
    # 做一些安装之前的hook
end

post_install do |installer| 
    # 做一些安装之后的hook
end
复制代码

Xcode工程有什么变化

在cocoaPods和Xcode工程进行集成的过程中,会有有以下流程

  • creat workspace
    创建xcworkspace文件。其实xcworkspace文件本质上只是xcodeproject的集合,数据结构如下:
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:Demo/Demo.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:Pods/Pods.xcodeproj">
   </FileRef>
</Workspace>
复制代码
  • create group 在工程中创建group文件夹,逻辑上隔离一些文件

  • create pod project & add pod library
    创建pod.xcodeproject工程,并且将在podfile中定义的第三方库引入到这个工程之中。

  • add embed frameworks script phase
    添加了[CP] Embed Pods Frameworks,相应的,多了pods_xxx的group,下列xxx.framework.sh,来完成将内部第三方库打包成.a静态库文件(在Podfile中如果选择了!use_frameworks,则此步骤会打包成.framework)

  • remove embed frameworks script phase
    如果本次podfile删除了部分第三方库,则此步骤会删除掉不需要的第三方库,将其的引用关系从Pod.xcodeproject工程中拿走。

  • add copy resource script phase
    如果第三方库存在资源bundle,则此步骤会将资源文件进行复制到集中的目录中,方便统一进行打包和封装。相应的,会添加[CP] Copy Pods Resources脚本。

  • add check manifest.lock script phase
    前文提到过,manifest.lock其实是podfile.lock的副本。此步骤会进行diff,如果存在不一致,则会提示著名的那句The sandbox is not sync with the podfile.lock错误。

  • add user script phase
    此步骤是对原有project工程文件进行改造。在运行过pod install后,再次打开原有工程会发现无法编译通过,因为已经做了改动。

    1.首先,添加了对Pod工程的依赖,具体为引用中多了libPods_xxx.a文件。此步骤的.a文件(或者.framework文件)为上述步骤中xxx.framework.sh打包出来的文件,也就是说,cocoaPods会把所有第三方的组件封装为一个.a文件(或者.framework文件)!

    图片[2]-cocoapods-一一网
    2. 建立了Pods的group,内含pods-xxx-debug.xconfig和pods-xxx.release.xconfig文件。这两个文件是对应工程的build phase的配置。相应的,主工程的Iinfo->Configurations的debug和release配置会对应上述两个配置文件。
    图片[3]-cocoapods-一一网
    3. 上述两个配置都做了什么?包括:
    Header_search_path,指向了Pod/Headers/public/xxx,添加了Pods文件编译后的头文件地址Other_LDFLAGS,添加了-ObjC等等,一些Pods变了,例如Pods_BUILD_DIR等

至此,原有xcode工程和新建的Pod工程完成了集成和融合。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享