Files
knowledge-kit/Chapter1 - iOS/1.51.md
2024-06-29 16:00:34 +08:00

11 KiB
Raw History

cocoapods 相关小技巧

1. 组件的地址

我们在做组件化的时候经常将一些业务模块封装打包,做成 pod 管理的形式,然后当在开发的时候需要修改一些模块化的代码。

当维护好组件的时候我们可能在一个新的工程设置好 podfile 引入组件,但是有可能需要继续修改组件的源代码,代码需要可编辑。所以我们可能需要将 Podfile 中的 pod 源修改为本地。 然后安装 pod install 后就可以看到在项目文件里面有可编辑的组件代码

pod 'GoodsCategoryModule', :path => '../GoodsCategoryModule'
#pod 'GoodsCategoryModule',:git => 'git@gitlab.xxx.com:forntend_ios/GoodsCategoryModule.git',:branch => 'release/Appstore'

注意点:

  1. 我们本地的组件源代码需要和 pod 文件中的代码工程文件名称一致
  2. 注释掉远端仓库的地址 path 后面是相对路径

2. 组件库使用 pch 头文件

///一个区分开发和线上环境的Log。NSLog的本质是调用对象方法的 description 方法所以线上代码使用NSLog会造成性能和安全问题
#ifdef DEBUG
    #define SafeLog(...) NSLog(__VA_ARGS__)
#else
    #define SafeLog(...)
#endif

所以我们需要对各个组件库里面的 NSLog 进行改造,变成 SafeLog。没做特殊处理,当你在 pod 库的 pch 文件写好代码,发现主工程执行 pod install 之后,看到之前写的 SafeLog 代码不见了。所以我们需要指定 pch 文件。 操作:

  • 新建 PrefixHeader.h 头文件
  • 在当前 pod 库的 ***.podspec 文件中写如下代码
  s.prefix_header_contents = '#import "PrefixHeader.h"'

说明:该代码的作用相当于将 PrefixHeader.h 文件写入到当前 pod 库 XQTriggerKit-prefix.pch 文件的最后一行。相当于

#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

#import "PrefixHeader.h"

缺点:项目存在多个 pod 组件库。主工程修改 pch 还好,但是每个 pod 库都去新建 PrefixHeader.h 文件和 podspec 文件添加一行代码。工作量非常大。

改进方案:对 NSLog 进行 Hook。

步骤:

  • 引入 fishhook 库
  • 新建 SafeLog.h 和 SafeLog.m 文件
  • 在 SafeLog.m 文件的 load 方法中对 NSLog 进行 hook判断是 Debug 环境就打印。代码如下
#import "SafeLog.h"
#import "fishhook.h"

// orig_NSLog是原有方法被替换后 把原来的实现方法放到另一个地址中
// new_NSLog就是替换后的方法了
static void (*orig_NSLog)(NSString *format, ...);

@implementation SafeLog

void(new_NSLog)(NSString *format, ...)
{
    va_list args;
    if (format) {
        va_start(args, format);
        NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
        #ifdef DEBUG
            orig_NSLog(@"%@", message);
        #endif
        va_end(args);
    }
}

+ (void)load
{
    rebind_symbols((struct rebinding[1]){ {"NSLog", new_NSLog, (void *)&orig_NSLog} }, 1);
}

@end

3. CocoPods 指定版本号带 ~> 与不带的区别

  • 带 ~> 是指库版本号的一个范围。大于等于指定的版本号,小于高一位的版本号
  • 不带 ~> 是指固定的版本号
pod 'aaa', '~> 0.1.2'  // 大于等于 0.1.2 且小于 0.2
pod 'bbb', '1.1' 版本号指定为 1.1

4. 查看当前 Pod 库被何处引用

出于某种原因经常会需要查看当前 Pod 库被何处引用了你需要修改组件库A然后A修改完之后可能需要在依赖组件库A的地方去修改版本号。这时候就需要查询了例子可能不优雅但是确实有需要查询引用的情况有轮子

  • 先下载所需要的库
gem install reversepoddependency
  • 利用脚本在我们的组件库 repo 中去查询
specbackwarddependency /Users/liubinpeng/.cocoapods/repos/51xianqu-xq_specs(本地CocoPod repo地址) xq_baaidumapkit(组件库名称)

鉴于这个命令比较长,且本人使用的是 iTerm2+Zsh所以将命令写入到 .zshrc 文件中, source 编译链接过可以快速使用。例如 repoanalysis xq_baaidumapkit

alias repoanalysis='specbackwarddependency /Users/liubinpeng/.cocoapods/repos/51xianqu-xq_specs' 

Pod组件库依赖分析

具体的操作步骤可以参考我的这个文章

5. lint 的时候安装一些神奇的依赖

在对 App 做应用包瘦身的时候发现了一些问题。某个组件库 lint 的时候通过终端的信息,发现安装了一些不是 podspec 里面指定的依赖仓库。百思不得其解,同事说可能是之前的某个版本依赖了这些项目。有了这个思路就好办事了。

遇到问题

  • 打开本地的 /Users/liubinpeng/.cocoapods/repos 文件夹。查看本地的私有 repo 的管理的所有的项目,找到出问题的 repo进去删掉有问题的 tag。
  • 出问题的 repo 将远端的有问题的 tag 也删掉
  • 删除远端的 repo 仓库的有问题的 tag。

6. lint 失败 - 报错为 error: invalid task ('StripNIB...

Build system information
    error: invalid task ('StripNIB /Users/liubinpeng/Library/Developer/Xcode/DerivedData/App-fgbnpsgtrtstroctiqnanvyrfwyr/Build/Products/Release-iphonesimulator/XQLoginModule/SDGMemberCardBindViewController.nib') with mutable output but no other virtual output node (in target 'XQLoginModule')

原因为 xib 和图片资源都属于资源文件不可以放在源文件Classes需要放在 Assets 中。如果放到 Classes 文件夹中 lint 会报错。

7. 一台电脑安装了最新版本的,出问题删除最新版 Xcode下载旧版本 Xcodepod install 失败

You need at least git version 1.8.5 to use CocoaPods
  • 可能是cocoapods安装成功了但是链接Xcode的版本过低所以需要更新Xcode
  • 电脑安装了多个版本的Xcode就需要修改链接Xcode路径改成链接电脑比较高版本的Xcode。
    sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer
    

8. 一台电脑安装了最新版本的,出问题删除最新版 Xcode下载旧版本 Xcode打开工程 Xib 报错

  • sudo killall -9 com.apple.CoreSimulator.CoreSimulatorService
  • xcrun simctl erase all

9. 开发电脑上安装了旧版本 Cocopods,Mac 系统升级后, pod lint 失败

解决方案: 将 fourflusher 仓库中上的 find.rb 文件中的 ruby 脚本复制到本地的 fourflusher 下.

查找本地 fourflusher 文件夹所在位置.

gem which fourflusher

我的电脑 find.rb 文件所在位置. /Library/Ruby/Gems/2.6.0/gems/fourflusher-2.3.1/lib/fourflusher/find.rb

注意: 文件保存需要权限,所以加 sudo

10. pod lint 产生的信息太多,一屏显示不全,但是出错之后我们可能需要去查看 error 信息,上下翻页不方便

解决方案: 利用脚本 >1.log 2>&1 将当前的 pod lint 产生的信息写入文件.

完整代码

 pod lib lint --sources=****,**** --allow-warnings --verbose --use-libraries >1.log 2>&1

11. pod 库每次修改代码,主工程必须 clean 再安装才可以看到新改动的代码

解决方案:

install! 'cocoapods', :disable_input_output_paths => true

12. pod 库太多,每次构建编译都很耗费时间

install! 'cocoapods', generate_multiple_pod_projects: true

13. 卸载旧版本 cocoapods 安装新的

 sudo gem uninstall cocoapods-core cocoapods cocoapods-deintegrate cocoapods-downloader cocoapods-plugins cocoapods-search cocoapods-stats cocoapods-trunk cocoapods-try coderay colored2 concurrent-ruby cocoapods-clean
sudo gem install cocoapods

14. pod 拉取代码太慢

在平时开发的时候,使用 cocoapods 拉取代码经常会比较慢,偶尔一次两次也还可以忍受,但是某些哭每次都很慢,而且 install 的时候工程被修改,没办法编译开发。所以需要想个办法解决这种问题。

其实知道 cocoapods 的工作原理的话,我们可以投机取巧。做过 SDK 开发的同学一般都知道,代码开发、测试完毕后需要 lint将 podspec 文件提交到中心。项目中 Podfile 中添加依赖描述,依赖可以用 分支、tag、path 等形式指定。

方法一:

所以明白怎么工作的,我们可以在本地搭建一个静态服务,专门用来提供较大 SDK 的下载,拿 NodeJS、python、Java、php 都可以,很快写一个静态服务。然后从本地 .cocoapods 里找到对应的 SDK 文件夹,修改 podspec 文件,修改 source 为本地服务器地址资源地址。

由于本地静态服务里面的资源可能会停留在较早版本,所以可以使用定时服务拉取 github 项目最新代码。crontab + shell 做这个很容易。

方法二:

前端有 jsdeliver针对 js/css 加速访问。但是它也支持 github 上的仓库。 但是包大小限制jsdeliver 规定单个文件不能大于 20M仓库的某版本不能大于 50M。所以可以对大文件进行 xz 压缩

Mac split 命令可以拆分包cat 可以合并包。

// 分割文件
split -b 10m xxxx.tar.xz xxxx.tar.xz .
// 查看效果
ll xxxx.xz*

// 合并
cat xxxx.tar.xz.* > xxxx.tar.xz

// 再次验证大小
ll xxxx.tar.xz

// 解压
mkdir unpackedDir
mv xxxx.tar.xz unpackedDir
cd unpackedDir
tar vxf xxxx.tar.xz

所以我们可以将较大的库拆分多个,部署到 jsdeliver然后使用的时候进行加速。 这一步可以是脚本自动完成,写脚本,将文件夹压缩,拆分,上传。

所以使用的时候类似

wget https://cdn.jsdelivr.net/mmm/xxxx.tar.xz.aa
wget https://cdn.jsdelivr.net/mmm/xxxx.tar.xz.ab
wget https://cdn.jsdelivr.net/mmm/xxxx.tar.xz.ac
cat xxxx.tar.xz.* > xxxx.tar.xz
tar xvf xxxx.tar.xz
rm xxxx.tar.xz.* xxxx.tar.xz

结合 cocoapods 的 prepare_command 使用。 比如

Pod::Spec.new do |s|
  s.name = 'xxxx'
  s.prepare_command = <<-CMD
    sh cat.sh
  CMD
end

15. pod lib create 报错 Ignoring ffi-1.16.3 because its extensions are not built

开始可能发现错误

Ignoring ffi-1.16.3 because its extensions are not built. Try: gem pristine ffi --version 1.16.3 类似这样的错误

sudo gem install --user-install rexml
sudo gem install --user-install xcodeproj