ios-第三方库引入之CocoaPods

ios-第三方库引入之CocoaPods


前篇

CocoaPods是iOS开发、macOS开发中的包依赖管理工具,效果如Java中的Maven,nodejs的npm。

CocoaPods是一个开源的项目,源码是用ruby写的,源码地址在GitHub上。

无论是做iOS开发还是macOS开发,都不可避免的要使用到一些第三方库,优秀的第三方库能够提升我们的开发效率。如果不使用包依赖管理工具,我们需要手动管理第三方包,包括但不限于:

  1. 将这些第三方库的源码拷贝到项目中
  2. 第三方库代码有可能依赖一些系统framework,我们需要把第三方库依赖的framework导入到项目中
  3. 当第三方库有更新时,需要将更新过的代码拷贝到项目中

以上工作虽然简单,但是如果项目中的第三方库较多,需要耗费大量的时间和精力。CocoaPods可以将我们从这些繁琐的工作中解放出来。


安装 CocoaPods

安装 CocoaPods 比较方便。通常情况下,macOS都安装了 ruby,直接使用 ruby 的 gem 命令即可安装 CocoaPods。

  1. 查看有没有安装ruby, 如果能正确的输出版本号,则说明 ruby 已经正确安装

    1
    2
    $ ruby --version
    ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin20]
  2. 查看gem的版本号, 并替换源

    1
    2
    $ gem --version
    3.0.3

    替换源. ruby的软件源 rubygems.org 使用的是亚马逊云的服务,国内普通网络是不能访问的。如果不能访问,可以将ruby的源换成国内淘宝的源

    1
    2
    3
    4
    5
    6
    $ gem sources --remove https://rubygems.org/ // 删除源
    $ gem sources -a https://gems.ruby-china.com/ // 添加 淘宝源

    $ gem source -l // 查看源
    *** CURRENT SOURCES ***
    https://gems.ruby-china.com/
    • 如果gem的版本号过低,安装CocoaPods可能会失败。所以在安装CocoaPods之前可以升级一下gem

      1
      2
      $ sudo gem update --system --verbose
      Latest version already installed. Done.
  3. 安装 CocoaPods

    1
    2
    3
    4
    $ sudo gem install cocoapods --verbose
    Fetching concurrent-ruby-1.1.8.gem
    ...
    33 gems installed
  4. 初始化 CocoaPods

    1
    2
    3
    $ pod setup --verbose
    // 等待过程可能有点长,成功后会看到
    Setup completed
    • 查看 CocoaPods 有没有安装成功, 搜索 MJExtension 第三方库看看

      1
      2
      3
      4
      $ pod --version
      1.10.1

      $ pod search MJExtension

      表示 pod 已经已经 ok 了.

      若果报错: [!] Unable to find a pod with name, author, summary, or description matching MJExtension, 先不用管, 直接进行下一步引入试试看能不能安装成功


CocoaPods 工作原理

CocoaPods的使用相对来说是比较简单的。那么CocoaPods是如何完成这些工作的?以及为何生成了一个Pods工程?

实际上,CocoaPods是将所有依赖的第三方库都放到了Pods项目中

所有的源码管理工作从住项目转移到了Pods项目中。

Pods项目最终会编译成一个libPods-项目名.a的文件,主项目只需要依赖这个.a文件即可。


引入 第三方库

  1. 在项目的指定模块引入 第三方库

    1. 在项目新建一个文件, 名为: Podfile, 内容如下 (规则参考: [Podfile 规则](#Podfile 规则))

      1
      2
      3
      4
      5
      platform :ios, '9.0'

      target 'UnityApi' do
      pod 'YYModel'
      end
      • target 就是要引入第三方库的 模块
      • pod 就是第三方库
    2. cd 到 Podfile 所在目录, 安装

      1
      $ pod install --verbose
      • 如果报错提示需要更新库, 需要先更新

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
                $ pod install --repo-update

        然后会自动生成一个 pods 工程

        ![](https://pic04.wilker.cn/20210203163033-1.webp)

        同时主工程也也会引入对应的静态库

        ![](https://pic04.wilker.cn/20210203163249-1.webp)

        此时的静态并不存在, 所以下一步是构建静态库

        3. 打开 pods 工程, 默认是没有 scheme, 创建需要的模块的 scheme (也就是 Pods-xxx, 库不用, 以为它会在模块编译时自动编译出来), 然后 cmd + b 编译出 静态库, 默认输出到项目中的 build 目录下

        构建时一定要注意 cpu 架构一定要一致, 否则编译项目时报错链接不到静态库, 如: `Library not found for -lUICKeyChainStore`

        ![](https://pic04.wilker.cn/20210203163953-1.webp)

        有两种方式可以引用到第三方库

        1. 将这几个编译出来的 静态库 移到 *UnityApi* 模块找的到的 `Products ` 的地方, 如 默认的是在 `DerivedData`:

        `/Users/wilker/Library/Developer/Xcode/DerivedData/Build/Products/Debug-iphonesimulator`

        ![](https://pic04.wilker.cn/20210203164312-1.webp)

        (如果不移动这个编译出来的库, 会报错: `library not found for -lPods-UnityApi`)

        2. (建议) 因为 pod 中的 scheme 默认是构建到项目的 `build` 目录下, 所以修改使用到第三方库的 target 的 `Products` 路径与 pod 的一致就行, 也就是修改 *build settigns -> build locations -> build products path* 为 `build` 目录

        ![](https://pic04.wilker.cn/20210801033235-1.webp)

        - 如果不生效, 将 *xcode -> preferences -> locations -> advanced* 中, 勾选 *shared folder*, 并改为 *build*, 然后重启编辑器构建

        ![](https://pic04.wilker.cn/20210801151023-1.webp)

        4. 在 *UnityApi* 模块下编写代码

        ```objective-c
        // model
        @interface User : NSObject
        @property UInt64 uid;
        @property NSString *name;
        @end

        @implementation User
        @end

        // 将 model 转成 字段
        User* usr = [User new];
        usr.name = @"hello";
        usr.age = 123;
        NSDictionary *dict = [usr yy_modelToJSONObject];
        NSLog(@"--- dict: %@", dict);

      结果:

      1
      2
      3
      4
      2021-02-03 16:48:57.523644+0800 MyIosTest[34885:15899485] --- dict: {
      age = 123;
      name = hello;
      }

      之后追加其他第三方库, 在 Podfile 增加其他第三方库, 重走引入流程即可.


三方库必须在 executable 模块中引入

比如: 写了个 api 库, api 库使用了 json 库, 而 executable 模块又是用了 api 库, 最终的引入方式是: executable 模块引入 api 库 和 json 库, 而 api 库不引入 json 库

  • 如: Unity-iPhone 是 executable 模块, MJExtension 是 json 库

    image-20220816172610199


踩坑

编译报错: 链接不到第三方库

错误: library not found for -lPods-UnityApi

其实就是没有把 pods 工程编译出对应的 静态库移到正确的目录下


运行时闪退, 找不到第三方库

报错: unrecognized selector sent to instance

解决办法: 在 build settings -> other linker flags 中加多一个 -ObjC

  • -ObjC: 这个flag告诉链接器把库中定义的OC类和Category或nib都加载进来,编译后app会变大,以为加载了很多不必要的文件导致可执行文件变大。但是如果静态库中有类和分类只有加入这个flag才行;但是当静态库中只有分类而没有类时,-ObjC就失效了,这时需要加-all_load 或 -force_load了

参考: https://cloud.tencent.com/developer/article/1624205


Podfile 规则

多 模块 组合 第三方库

  • 不同项目引入不同的第三方库 - https://stackoverflow.com/questions/14906534/how-do-i-specify-multiple-targets-in-my-podfile-for-my-xcode-project

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    platform :ios, '9.0'
    inhibit_all_warnings!
    use_frameworks!

    def shared_pods01
    pod 'YYModel', '~> 1.0.4'
    pod 'UICKeyChainStore', '~> 2.2.1'
    pod 'AFNetworking', '~> 4.0'

    ########### Facebook
    pod 'FBSDKCoreKit', '~> 9.0.1'
    pod 'FBSDKLoginKit', '~> 9.0.1'
    pod 'FBSDKShareKit', '~> 9.0.1'
    end

    target 'MyIosTest' do
    shared_pods01
    end

    target 'MyIosTestTests' do
    shared_pods01
    end

    target 'UnityApi' do
    shared_pods01
    end

    target 'UnityApiTests' do
    shared_pods01
    end

版本号指定

需要导入一个第三方库,只需要

1
pod 'package name', 'version number'

即可。版本号有多种表示方式,这里简单介绍几种:

  • ‘1.0’ 只要该版本

  • ‘>=1.0’ 最低版本号为1.0

  • ‘<=1.0’ 最高版本号为1.0

  • >1.0’ 兼容1.0的版本的最新版本, 也就是 1.02.0 之间

通常情况下使用 ~> 的方式。


参数说明

参数

  • inhibit_all_warnings!

    inhibit_all_warnings! 屏蔽所有来自于cocoapods依赖库的警告。你可以全局定义,也能在子target里面定义,也可以指定某一个库:

  • use_frameworks!

    通过指定use_frameworks!要求生成的是framework而不是静态库。

    如果使用use_frameworks!命令会在Pods工程下的Frameworks目录下生成依赖库的framework

    如果不使用use_frameworks!命令会在Pods工程下的Products目录下生成.a的静态库

    !!!!!!!!!!!!!!!!!!!! 修改这个参数后, 要把 pod 工程 clear 一下, 再编译, 否则可能出现 还是 编译出 .a, 而不是 framework !!!!!!!!!!!!!!!!!!!!


CocoaPods 相关的其他操作

  • 增加新的第三方

    如果使用过程中我还想添加其他的第三方怎么办,只要在Podfile里面接着添加,然后终端再执行pod install就可以了。

  • 更新CocoaPods中的第三方们。

    第三方库们都有人在维护升级,我们需要隔断时间就要更新下我们工程中第三方库的版本。只需要终端输入命令pod update就可以了。

    如果遇到pod install或者pod update慢的问题,原因在于当执行以上两个命令的时候会升级CocoaPods的spec仓库,加一个参数可以省略这一步,然后速度就会提升不少。加参数的命令如下:
    pod install --verbose --no-repo-update
    pod update --verbose --no-repo-update

  • 删除CocoaPods中的某些第三方们。

    当我们需要去掉某个第三方库时,只需要在Podfile删除该引入该库的语句,然后执行pod update或者pod install就可以了。

  • 将CocoaPods从项目中删除

    如果你在以后的使用过程中不想用CocoaPods了怎么办?很简单,把多出来的东西们都删掉就可以了,不过为了项目正常运行,你需要手动导入已经使用的第三方们哦。

  • 升级CocoaPods

    升级CocoaPods版本的命令和安装CocoaPods的命令一样,都是sudo gem install cocoapods
    如果老版本升级cocoapods的时候提示Operation not permitted - /usr/bin/xcodeproj,改用命令sudo gem install -n /usr/local/bin cocoapods --pre就可以了。

  • 卸载CocoaPods

    卸载CocoaPods的命令是sudo gem uninstall cocoapods

    执行完命令后,最下面打印Successfully uninstalled cocoapods字样就代表已经成功卸载了。