随着业务的扩展、项目体积的增大,CocoaPods
组件库愈来愈多,每次从新编译的时候速度愈来愈慢,这给咱们提出了须要提升编译速度的需求。html
为了提升项目编译速度,对于大量使用组件化开发的项目组而言,组件二进制化是必然要走的路线,虽然中心思想就是要将各个组件打包成.a
二进制库,可是各个公司可能方案都不太相同,网上的方案也有不少可供选择,这里我大致总结成如下几种:ios
Carthage
管理podspec
环境变量(宏管理)podspec
分tag
管理(只针对私有库)前两个就不在这里讨论了能够看看这篇讲解。今天重点给你们分享一下第三和第四种方案的实施,可是目前只能针对私有库实施,对于一些第三方的公有库目前没有什么好的方案(😁 有好方法的同窗能够在评论区推荐一下)。git
😝 若是您对这一块很了解请跳过这一步直接看第二步github
对于私有库的建立,通常咱们会采用pod lib create XXX
模板来进行构建(若是还不知道这条命令是干吗的同窗能够先移步了解一下理解CocoaPods的Pod Lib Create)面试
这里咱们拿ABC
这个项目进行举例,首先咱们执行pod lib create ABC
建立ABC
的私有库 CocoaPods
会从https://github.com/CocoaPods/pod-template.git
下载模板文件,并询问你一些构建信息,正常填就行了。xcode
做为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个个人iOS交流群:413038000,无论你是大牛仍是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 你们一块儿交流学习成长!缓存
[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 ] > 复制代码
通常若是咱们构建好了的话工程目录会相似这样一个结构:ruby
. ├── 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
、源文件目录、Podfile
、podspec
、.gitignore
文件等(真是一个贴心的小家伙),并且很规范,Demo
文件在Example
目录下bash
窥视一下podspec
文件你就明白了源码须要指定在./Classes/**/*
路径下运维
s.source_files = 'ABC/Classes/**/*' 复制代码
为了演示效果,咱们建立两个源文件ABC.h
与ABC.m
并放入Classes
路径下,同时将默认的ReplaceMe.m
删除
[图片上传中...(image-b08f0c-1594108050326-4)]
接着在Example
下执行pod install
,能够发现ABC.h/m
已经导入成功
[图片上传中...(image-e75b0-1594108050326-3)]
至此,咱们就明白了私有库的建立过程,须要编写源代码须要放入指定目录下并在执行pod install
进行同步
组件二进制其实指的就是打包成动态库/静态库,因为过多的动态库会致使启动速度减慢得不偿失,此外iOS
对于动态库的表现形式只有framework
,若想作源码与二进制切换时,引入头文件的地方也不得不进行更改,例如:
import <ABC.h> // 源码引用 import <ABCBinary/ABC.h> // 动态库引用 复制代码
而打包成静态库.a
文件(注意不要打包成framework
形式)则不须要更改引用代码,因此综上所述,咱们选择打包成静态库的方式不需修改引用代码、缩小体积提高编译速度。
肯定目标以后,就是实施了,通常而言咱们私有库都会在远程托管地址有git
仓库,而后再上传到指定的私有源(specs)上,那么就会引伸出几个问题:
git
(若是包体积很大会很占用git
空间)针对这几个问题,一一回答:
其实这个很简单,咱们接着拿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
,只作引用便可
[图片上传中...(image-d11b04-1594108050325-2)]
[图片上传中...(image-a6b089-1594108050324-1)]
以后咱们须要到ABCBinary
的Build Setting
中指定静态库所能运行的最低版本:
Build Setting->Deployment->iOS Deployment Target 复制代码
并在Build Phases
中指定头文件,将ABC.h
拖入Public中,具体步骤:
TARGETS->ABCBinary->Build Phases->New Header Phase 复制代码
至此咱们完成了一套代码管理二进制与源码,但有个小细节须要注意:就是若是源代码有变更须要在XXXBinary
文件中从新导入一遍,否则二进制的文件不会自动更新(同窗们有好的建议能够评论区讨论下)
其实git
对代码管理时会将不一样的diff
作备份(在.git
这个文件夹下),可是对于二进制文件来讲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
仓库空间压力了(运维小哥哥们表示“尼玛松了一口气”)
在提高编译速度的前提下,还须要考虑到能随时进行源码调试,这就涉及到了如何在源码与二进制间切换的问题,网上的思路有不少:环境变量、白名单、tag切换等。
这几种方式在前言部分咱们已经讲过了,接下来咱们介绍一下“环境变量”和“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 = <<-DESC TODO: 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/<TWITTER_USERNAME>' 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/**/*' end end 复制代码
让咱们来看看效果,在Example
下执行pod install
,发现切换过来了,Nice 😝~
[图片上传中...(image-84413-1594108050322-0)]
接下来验证本地podspec
(如有问题按照提示更改,ssh://xxx.git
是你私有源的地址):
pod lib lint --sources=ssh://xxx.git --allow-warnings --verbose --use-libraries 复制代码
若没问题,在ABC
的git
仓库打一个0.1.0
的版本tag
,并上传ABC.podspec
至私有源,上传成功后修改podspec.version
为0.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
中若是某些库没有指定版本就会更新到最新版本)
Ruby语法支持一些环境变量的读取,所以能够在pod install
时增长参数以此判断是否要切换源码:
IS_BINARY=1 pod install # 1 表明二进制 IS_BINARY=0 pod install # 0 表明源码 pod install # 默认也是0 源码 复制代码
在podspec
中作修改:
Pod::Spec.new do |s| s.name = 'ABC' s.version = '0.1.0.Binary' s.summary = 'A short description of ABC.' s.description = <<-DESC TODO: 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/<TWITTER_USERNAME>' s.ios.deployment_target = '8.0' if s.version.to_s.include?'Binary' or ENV['IS_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/**/*' end end 复制代码
同tag切换同样,这种方式在实际项目中切换也存在问题,须要两个必要步骤:
pod cache clean ABC # 先清理ABC的pod缓存 rm Pods/ABC # 再把ABC从实际项目中的Pods目录下移除 复制代码
方式 | 优势 | 缺点 |
---|---|---|
Ruby环境变量切换 | 一、不须要上传两份podspec | |
二、切换时不须要修改Podfile | 一、须要清除私有库的缓存 |
二、须要手动删除/Pods/XXX
三、不能针对单独库进行切换,除非自定义白名单之类的规则 |
| tag切换 | 一、能够针对单独某个库进行切换 | 一、须要执行pod update(需等待repo master源的更新)
二、私有库的tag须要打两个,podspec上传时须要传两次
三、切换时须要手动修改Podfile文件的版本信息 |
好,至此iOS组件二进制方案就介绍完了,咱们经过ABC
项目的实践了解了整个过程:
.gitignore
忽略输出的二进制包pod update
或删除Podfile.lock
中相关库信息本文demo相关连接以下,另附自动上传podspec脚本地址(相关文章),喜欢的朋友点个star
做为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个个人iOS交流群:413038000,无论你是大牛仍是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 你们一块儿交流学习成长!
做者:被帅醒的吴宝宝
连接:http://www.javashuo.com/article/p-ypjefvoa-kn.html