CocoaPod知识整理

作者: 落影loyinglin | 来源:发表于2019-07-11 20:08 被阅读419次

    前言

    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文件。

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

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

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

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

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

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

    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
    

    类似的,还有很多其他用ruby去实现的feature。

    二、Pods目录

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

    目录的组成部分:

    • 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工程的时候,会检查这两个文件是否一致。

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

    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名)

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

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

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

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

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

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

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

    对于资源文件,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。

    附录

    CocoaPods使用总结
    基于 CocoaPods 进行 iOS 开发
    pod install vs. pod update
    CocoaPods 都做了什么?

    相关文章

      网友评论

        本文标题:CocoaPod知识整理

        本文链接:https://www.haomeiwen.com/subject/hjoecftx.html