ios传感器应用开发最佳实践_iOS开发 如何实践组件二进制方案?

欢迎点击上方蓝字“iOSer”关注我们 再点击右上角“...”菜单,选择“设为星标 我们一起精进、成长! ed6305928d763f16a1f66e266f0c2708.png 本文由掘金优秀作者 被帅醒的吴宝宝 授权发表 原文链接 | https://juejin.im/post/5efaf0655188252e42157e8a

随着业务的扩展、项目体积的增大,CocoaPods组件库越来越多,每次重新编译的时候速度越来越慢,这给我们提出了需要提高编译速度的需求。

为了提高项目编译速度,对于大量使用组件化开发的项目组而言,组件二进制化是必然要走的路线,虽然中心思想就是要将各个组件打包成.a二进制库,但是各个公司可能方案都不太相同,网上的方案也有很多可供选择,这里我大体总结成以下几种:

  • 分仓库管理
  • Carthage管理
  • podspec环境变量(宏管理)
  • podspectag管理(只针对私有库)
前两个就不在这里讨论了可以看看这篇讲解。 今天重点给大家分享一下第三和第四种方案的实施,但是目前只能针对私有库实施,对于一些第三方的公有库目前没有什么好的方案( ? 有好方法的同学可以在评论区推荐一下 )。

实施

1、创建pod私有库

? 如果您对这一块很了解请跳过这一步直接看第二步 对于私有库的创建,一般我们会采用 pod lib create XXX 模板来进行构建(如果还不知道这条命令是干嘛的同学可以先移步了解一下 理解CocoaPods的Pod Lib Create ) 这里我们拿 ABC 这个项目进行举例,首先我们执行 pod lib create ABC 创建 ABC 的私有库  CocoaPods 会从 https://github.com/CocoaPods/pod-template.git 下载模板文件,并询问你一些构建信息,正常填就好了。
[MichaeldeMacBook-Pro:~ michaelwu$ pod lib create ABC
Cloning `https://github.com/CocoaPods/pod-template.git` into `ABC`.
Configuring ABC template.------------------------------
To get you started we need to ask a few questions, this should only take a minute.
If this is your first time we recommend running through with the guide: - https://guides.cocoapods.org/making/using-pod-lib-create.html( hold cmd and double click links to open in a browser. )
What platform do you want to use?? [ iOS / macOS ]>
一般如果我们构建好了的话工程目录会类似这样一个结构:
.
├── ABC
│ ├── Assets
│ └── Classes
├── ABC.podspec
├── Example
│ ├── ABC
│ ├── ABC.xcodeproj
│ ├── ABC.xcworkspace
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ └── Tests
├── LICENSE
├── README.md
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
这里你会发现, CocoaPods 已经帮我们创建好了 Demo 、源文件目录、 Podfilepodspec.gitignore 文件等(真是一个贴心的小家伙),而且很规范, Demo 文件在 Example 目录下 窥视一下 podspec 文件你就明白了源码需要指定在 ./Classes/**/* 路径下
 s.source_files = 'ABC/Classes/**/*'
为了演示效果,我们创建两个源文件 ABC.hABC.m 并放入 Classes 路径下,同时将默认的 ReplaceMe.m 删除 5dd0a94f2a5a866bcee572bf0765bef5.png 接着在 Example 下执行 pod install ,可以发现 ABC.h/m 已经导入成功 29e2d28fa2fe7de6f3c3a4e301b13fc1.png 至此,我们就明白了私有库的创建过程,需要编写源代码需要放入指定目录下并在执行 pod install 进行同步

2、创建静态库

组件二进制其实指的就是打包成动态库/静态库,由于过多的动态库会导致启动速度减慢得不偿失,此外 iOS 对于动态库的表现形式只有 framework ,若想做源码与二进制切换时,引入头文件的地方也不得不进行更改,例如:
import <ABC.h> // 源码引用import <ABCBinary/ABC.h> // 动态库引用
而打包成静态库 .a 文件(注意不要打包成 framework 形式)则不需要更改引用代码,所以综上所述,我们选择打包成静态库的方式不需修改引用代码、缩小体积提升编译速度。 确定目标之后,就是实施了,一般而言我们私有库都会在远程托管地址有 git 仓库,然后再上传到指定的私有源(specs)上,那么就会引申出几个问题:
  • 要不要将静态库上传到git(如果包体积很大会很占用git空间)
  • 怎么做到一套代码同时管理源码和二进制
  • 为了能够调试源码,如何在源码及二进制间切换(下一步骤会讲到)
针对这几个问题,一一回答:

3、静态库与源码如何用同一套代码管理?

其实这个很简单,我们接着拿 ABC 这个项目举例子,进入 Example 打开我们的 ABC.xcworkspace 工程,然后创建新的 Target 为静态库,并取名为 ABCBinary (一定要取这个名字,后面我会解释)
File->New->Target->Static Library
此时在 Example 目录下会增加刚刚创建的 Target 文件夹,结构如下:
├── ABCBinary
│ ├── ABCBinary.h
│ └── ABCBinary.m
Xcode默认会帮我们生成两个文件,我们将 .h 改名为 placeholder.h.m 删除,这里为什么要将 .h 换成 placeholder.h 呢?先卖个关子,待会我们再作解释。 我们把刚才写的 ABC.h/m 的源码拖到 ABCBinary 中,注意不要勾选 Copy items if needed ,只做引用即可 0d27786c66c03d7d0df3fbb57a5f7881.png 697c7ba82ab12ff124a0325e79ae0932.png 之后我们需要到 ABCBinaryBuild Setting 中指定静态库所能运行的最低版本:
Build Setting->Deployment->iOS Deployment Target
并在 Build Phases 中指定头文件,将 ABC.h 拖入Public中,具体步骤:
TARGETS->ABCBinary->Build Phases->New Header Phase
至此我们完成了一套代码管理二进制与源码,但有个小细节需要注意:就是如果源代码有变动需要在 XXXBinary 文件中重新导入一遍,不然二进制的文件不会自动更新(同学们有好的建议可以评论区讨论下)

3、是否需要将二进制上传至git?

其实 git 对代码管理时会将每一次的 commit 做备份(在 .git 这个文件夹下),以便于随时代码回滚,倘若我们每次都对私有库进行更新时都将二进制包传至 git ,那么时间久了无疑是对 git 仓库空间的一个挑战(如果你们公司空间足够大不需要考虑,那么请忽略这一步) 网上有很多针对这个问题给出的解决方案,但都不是很完美,大体上都是说将 二进制包 单独传到另一份静态资源地址,以此解决 git 过大问题,不过我觉得没有解决痛点,能不能不上传二进制包呢? 结论当然是可以, CocoaPods 本地的缓存目录在
~/Library/Caches/Cocoapods
其实每次我们更新 pod 库时, CocoaPods 都会先从指定源去拉源代码再根据该库的 podspec 文件指定输出目标文件,那么我们如果能把静态库打包推迟到 pod install 阶段就不需要上传二进制包到 git 了,但是如何做到延迟打包呢? 很幸运, CocoaPods 提供了针对 podspec 的预执行脚本, prepare_command(戳我进官网) 命令,该命令可以指定相应的脚本在 pod install 时去执行,那么我们就可以将编译打包的脚本放入其中,从而完成延迟打包 好了,理论上貌似可行了,实践出真知啊(? 绝对不能做一个理论性选手啊),具体怎么做? 首先我们需要一个能一键打静态库包的脚本(一刀99级那种),帅气的我这边已经为大家准备好了,只修改一下 PROJECT_NAME 即可,拷贝脚本至根目录并赋予执行权限:
# 当前项目名字,需要修改!
PROJECT_NAME='ABC'
# 编译工程
BINARY_NAME="${PROJECT_NAME}Binary"
cd Example
INSTALL_DIR=$PWD/../Pod/Products
rm -fr "${INSTALL_DIR}"
mkdir $INSTALL_DIR
WRK_DIR=build
BUILD_PATH=${WRK_DIR}
DEVICE_INCLUDE_DIR=${BUILD_PATH}/Release-iphoneos/usr/local/include
DEVICE_DIR=${BUILD_PATH}/Release-iphoneos/lib${BINARY_NAME}.a
SIMULATOR_DIR=${BUILD_PATH}/Release-iphonesimulator/lib${BINARY_NAME}.a
RE_OS="Release-iphoneos"
RE_SIMULATOR="Release-iphonesimulator"
xcodebuild -configuration "Release" -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${BINARY_NAME}" -sdk iphoneos clean build CONFIGURATION_BUILD_DIR="${WRK_DIR}/${RE_OS}" LIBRARY_SEARCH_PATHS="./Pods/build/${RE_OS}"
xcodebuild ARCHS=x86_64 ONLY_ACTIVE_ARCH=NO -configuration "Release" -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${BINARY_NAME}" -sdk iphonesimulator clean build CONFIGURATION_BUILD_DIR="${WRK_DIR}/${RE_SIMULATOR}" LIBRARY_SEARCH_PATHS="./Pods/build/${RE_SIMULATOR}"
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -rp "${DEVICE_INCLUDE_DIR}" "${INSTALL_DIR}/"
INSTALL_LIB_DIR=${INSTALL_DIR}/lib
mkdir -p "${INSTALL_LIB_DIR}"
lipo -create "${DEVICE_DIR}" "${SIMULATOR_DIR}" -output "${INSTALL_LIB_DIR}/lib${PROJECT_NAME}.a"
rm -r "${WRK_DIR}"
我们还是拿 ABC 的项目来接着实践,拷贝脚本后,先来看一下我们 ABC 目前的结构:
.
├── ABC
│ ├── Assets
│ └── Classes
├── ABC.podspec
├── Example
│ ├── ABC
│ ├── ABC.xcodeproj
│ ├── ABC.xcworkspace
│ ├── ABCBinary
│ │ └── placeholder.h
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ └── Tests
├── LICENSE
├── README.md
├── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
└── build_lib.sh
可以看到最下面多了一个 build_lib.sh 脚本(就是刚刚拷贝的那个脚本),另外 ABCBinary 里面有一个 placeholder.h ,这里解释一下之前埋下的悬念:因为 ABCBinary 文件夹里对于源码的引用没有 copy ,所以在提交到 git 时会自动将文件夹清空(也就是说在git目录里找不到),因此需要加一个占位防止文件夹不上传到 git ,但是切记不要编译到静态库里! 好的,至此一键打包脚本也准备好了,通过查看脚本我们发现这个二进制包最终会输出到根目录下的 ./Pod/Products/ 目录中,那不还是得传到 git 吗?别急,你忘了 gitignore 了吗? 配置 .gitignore 忽略 Pod/ 文件不就行了嘛,在 .gitignore 最下面增加忽略
Pod/
好了至此,我们完成了自动打包脚本及 git 忽略二进制包,再也不用担心我们的 git 仓库空间压力了(运维小哥哥们表示“尼玛松了一口气”)

4、如何在源码与二进制间切换

在提升编译速度的前提下,还需要考虑到能随时进行源码调试,这就涉及到了如何在源码与二进制间切换的问题,网上的思路有很多:环境变量、白名单、tag切换等。 这几种方式在前言部分我们已经讲过了,接下来我们介绍一下“环境变量”和“tag切换”这两种方式:

4.1、 如何利用tag进行切换:

首先我们需要约定好规则:当 version 中包含 .Binary 关键字时执行 prepare_command 命令并输出 source 为静态库,具体操作如下( podspec 是用 ruby 写的,支持条件判断):
if s.version.to_s.include?'Binary'
puts '-------------------------------------------------------------------'
puts 'Notice:ABC is binary now'
puts '-------------------------------------------------------------------'
s.prepare_command = '/bin/bash build_lib.sh'
s.source_files = 'Pod/Products/include/**'
s.ios.vendored_libraries = 'Pod/Products/lib/*.a'
s.public_header_files = 'Pod/Products/include/*.h' else
s.source_files = 'ABC/Classes/**/*'end
由于 tag 是根据 version 走的( tag => s.version.to_s ),因此只需要我们修改 s.version = '0.1.0.Binary' 即可实现二进制打包 好,我们贴一段此时 ABC.podspec 完整的代码:
Pod::Spec.new do |s|
s.name = 'ABC'
s.version = '0.1.0.Binary'
s.summary = 'A short description of ABC.'
s.description = <DESCTODO: Add long description of the pod here.DESC
s.homepage = 'https://github.com/609223770@qq.com/ABC'# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { '609223770@qq.com' => '609223770@qq.com' }
s.source = { :git => 'https://github.com/609223770@qq.com/ABC.git', :tag => s.version.to_s }# s.social_media_url = 'https://twitter.com/'
s.ios.deployment_target = '8.0'if s.version.to_s.include?'Binary'
puts '-------------------------------------------------------------------'
puts 'Notice:ABC is binary now'
puts '-------------------------------------------------------------------'
s.prepare_command = '/bin/bash build_lib.sh'
s.source_files = 'Pod/Products/include/**'
s.ios.vendored_libraries = 'Pod/Products/lib/*.a'
s.public_header_files = 'Pod/Products/include/*.h' else
puts '-------------------------------------------------------------------'
puts 'Notice:ABC is source code now'
puts '-------------------------------------------------------------------'
s.source_files = 'ABC/Classes/**/*'endend
让我们来看看效果,在 Example 下执行 pod install ,发现切换过来了,Nice ?~ 22d044afc89ac4b210dc82f8599830d9.png 接下来验证本地 podspec (若有问题按照提示更改, ssh://xxx.git 是你私有源的地址):
pod lib lint --sources=ssh://xxx.git --allow-warnings --verbose --use-libraries
若没问题,在 ABCgit 仓库打一个 0.1.0 的版本 tag ,并上传 ABC.podspec 至私有源,上传成功后修改 podspec.version0.1.0.Binary 再次执行上传:
pod repo push XXXSpecs ABC.podspec --allow-warnings --verbose --use-libraries
✅ 如果一切顺利,我们已经将Binary和源码的 ABC 上传到了私有源。 接下来我们在实际项目实验一下, Podfile 中指定,并执行安装
pod 'ABC', '~> 0.1.0' # source code
pod install
不出意外源码 ABC 安装成功,这时我们修改 tag 版本后面加 .Binary ,再次执行 pod install ,如下所示:
pod 'ABC', '~> 0.1.0.Binary' # source code
pod install
很遗憾,你可能会发现源码并没有切换成功,为什么呢? 原来 Pod 的版本管理是放在 Podfile.lock 中,每次执行 pod install 时若 Podfile.lock 中已经存在此库,则只下载 Podfile.lock 文件中指定的版本进行安装,否则去搜索这个 pod 库在 Podfile 文件中指定的版本来安装。 因此,解决办法有两种,一种是从 Podfile.lock 中将包含 ABC 的地方全部删除或是干脆直接删除 Podfile.lock ,再次执行 pod install 会发现切换变过来了。 还有一种方法是执行 pod update ,这也是 update 和 install 的区别,update会读取 Podfile 中的版本去更新 Podfile.lock 文件。( 戳我查看pod install和pod update区别 )
pod update ABC
执行后,先是会更新一下master和其他私有源,再去更新 ABC ,发现此时切换成功。(缺点就是如果 Podfile 中如果某些库没有指定版本就会更新到最新版本)

4.2、如何利用Ruby环境变量进行切换:

Ruby语法支持一些环境变量的读取,因此可以在 pod install 时增加参数以此判断是否要切换源码:
IS_SOURCE=1 pod install # 1 代表源码
IS_SOURCE=0 pod install # 0 代表二进制
pod install # 默认也是二进制
podspec 中做修改:
Pod::Spec.new do |s|
s.name = 'ABC'
s.version = '0.1.0.Binary'
s.summary = 'A short description of ABC.'
s.description = <DESCTODO: Add long description of the pod here.DESC
s.homepage = 'https://github.com/609223770@qq.com/ABC'# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { '609223770@qq.com' => '609223770@qq.com' }
s.source = { :git => 'https://github.com/609223770@qq.com/ABC.git', :tag => s.version.to_s }# s.social_media_url = 'https://twitter.com/'
s.ios.deployment_target = '8.0'if ENV['IS_SOURCE'] #s.version.to_s.include?'Binary'
puts '-------------------------------------------------------------------'
puts 'Notice:ABC is source code now'
puts '-------------------------------------------------------------------'
s.source_files = 'ABC/Classes/**/*'else
puts '-------------------------------------------------------------------'
puts 'Notice:ABC is binary now'
puts '-------------------------------------------------------------------'
s.prepare_command = '/bin/bash build_lib.sh'
s.source_files = 'Pod/Products/include/**'
s.ios.vendored_libraries = 'Pod/Products/lib/*.a'
s.public_header_files = 'Pod/Products/include/*.h'endend
同tag切换一样,这种方式在实际项目中切换也存在问题,需要两个必要步骤:
pod cache clean ABC # 先清理ABC的pod缓存
rm Pods/ABC # 再把ABC从实际项目中的Pods目录下移除

5、对比两种方式

方式 优点 缺点
Ruby环境变量切换1、不需要上传两份podspec
2、切换时不需要修改Podfile1、需要清除私有库的缓存
2、需要手动删除/Pods/XXX
3、不能针对单独库进行切换,除非自定义白名单之类的规则 |
| tag切换 | 1、可以针对单独某个库进行切换 | 1、需要执行pod update(需等待repo master源的更新)
2、私有库的tag需要打两个,podspec上传时需要传两次
3、切换时需要手动修改Podfile文件的版本信息 |

6、总结

好,至此切换 tag 方式的组件二进制方案就介绍完了,我们通过 ABC 项目的实践了解了整个过程:
  • 创建pod私有库
  • 在私有库Demo中创建静态库target,并配置头文件及最低iOS版本支持
  • 创建打包脚本
  • 设置.gitignore忽略输出的二进制包
  • 配置podspec根据tag版本判断或根据环境变量判断
  • 验证并上传源码及二进制的podspec
  • 在实际项目中切换时需要执行pod update或删除Podfile.lock中相关库信息

7、链接

本文demo相关链接如下,另附自动上传podspec脚本地址( 相关文章 ),喜欢的朋友点个star
  • 组件化方案demo地址:CocoaPodsBinary
  • 自动上传podspec脚本:upload_podspec
文章来源于网络,已尽可能标明作者以及来源,文章内容为作者独立观点,不代表本公众号立场,因文章侵权本公众号不承担任何法律及连带责任,如有侵权,请联系我们删除。
7597e35033e728bceb49ba3edf779935.png 觉得不错的话,别忘了点个''三连'' 哦~ ?
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值