智人能在残酷的进化大战中存活下来,缘由之一就是智人懂得将知识沉淀成外物,辅助彼此之间的合做,从而使得整个群体产生了规模效应,即1+1>2的效果。 从一个角度上说,石器时代是基于石器的组件化的时代,由于老张家的石矛(或其它石头利器)借给了老王,同样能够拿去狩猎。要想实现这个目的,必定要保证:html
一种观点认为,信息时代是基于软件构建起来的,由工程师不断贡献智力和体力,从而产生价值的时代。产品需求就好像前文说到的猎物,完成需求相似于成功捕杀猎物,而产品逾期就比如被猎物吃掉。所以在这个时代,也须要一些能够好用且容易使用的功能代码段,方便程序员拿来快速实现需求,就比如远古时代的能够复用的石矛。制做这种功能代码段的过程叫作组件化,这种方法带来的产出叫作组件,俗称轮子。ios
从本质上说,组件是经过库的方式来进行封装从而提供给开发者使用。而库,就是一种组织一个或多个文件的方式。在 iOS 8 以前,iOS 只支持以静态库的方式来使用第三方的代码。c++
静态库,在iOS中会被打包成.a文件,配合.h头文件一块儿能够完成功能的调用。可是在在概念上,静态库是一种All In One的设计思路,由于依赖静态库的代码会把静态库彻底连接到App的可执行文件中。也就是说,静态库是在编译器被连接到App中的,所以若是多个App都引用了同一个静态库,则每一个App都会把这个静态库连接一份,这其实浪费了内存。 固然,静态库的缺点不止于此。在使用静态库时,必须手动一个个连接它依赖的外部库,例如早期微信支付SDK的静态库接入方法中,必需要手动连接上:git
SystemConfiguration.framework,
libz.dylib,
libsqlite3.0.dylib,
libc++.dylib,
Security.framework,
CoreTelephony.framework,
CFNetwork.framework
复制代码
有没有一种须要轮流背诵蒸羊羔、蒸熊掌、蒸鹿尾儿、烧花鸭、烧雏鸡、烧子鹅、卤猪。。。
的既视感。 并且,静态库的特色致使了App每次启动时都要从新加载静态库的内存,没法控制加载时机,并且每次启动都须要从新加载静态库,致使二次加载时间没法被优化。 大部分时候,还须要在Other Linker Flags里填入Objc -all_load
来确保静态库正常工做。 好吧,听起来静态库很难用。 咱们都知道,后期iOS支持了动态库。那动态库是否是就能完美解决问题了呢?程序员
动态库,大部分会被打包成.tbd文件或者.dylib文件。不一样于静态库在编译期连接到App,动态库是在运行时连接到App的,所以它有了三个好处:github
不过,苹果对动态库的彻底支持仅停留在系统的动态库上,例如UI.framework,对于第三方的动态库,仍是须要embed到系统中。早期的一些热更新框架,例如JSPatch钻了漏子经过dlopen来进行热更新,不过很快被禁掉了。 不过,若是是企业证书,仍是能够在本身的app里灵活的加载第三方动态库的。算法
在解释静态库和动态库的过程当中,我并无提framework的字眼。有些开发者以为framework文件就是动态库,其实并不许确。 咱们提到的framework,指的是.framework文件,这既不必定是静态库,也不必定是动态库。实际上这是一种打包方式,将Header(头文件)、Binary(二进制代码文件)和bundle(资源文件)一块儿打包,方便开发者进行接入和调用。 所以framework究竟是静态库仍是动态库,取决于Binary文件(Mach-O文件)究竟是静态库仍是动态库。sql
“老一辈”的iOS开发都会记得手动引入静态库时,那无止境的编译错误。我简单总结一下,若是手动引入静态库,须要:xcode
程序员的创造力不少时候来源于“懒”,终于,CocoaPods横空出世,今后开启了一行命令行完成模块集成的时代!bash
CocoaPods是iOS平台当前最流行的包管理工具,能够将它理解为一个能够自动部署到项目的组件池,而对应的podfile文件就至关于请求组件的Request。当组件下载到工程后,cocoaPods会自动完成组件集成到现有项目的工做,并完成修改.xcodeproj文件和建立.xcworkspace文件。最终将全部组件统一打包成Pods.framework静态库,供项目使用。
在CocoaPods中,会存在如下几种文件:
pod install
,那具体在执行pod install
时发生了什么呢?当咱们运行pod install
时,会发生:
The sandbox is not sync with the podfile.lock
这种错误,则表示manifest.lock和podfile.lock文件不一致),此时通常须要从新运行pod install
命令。Embed Pods Frameworks
Copy Pod Resources
其中,pre-install hook和post-install hook能够理解成回调函数,是在podfile里对于install以前或者以后(生成工程可是还没写入磁盘)能够执行的逻辑,逻辑为:
pre_install do |installer|
# 作一些安装以前的hook
end
post_install do |installer|
# 作一些安装以后的hook
end
复制代码
git clone xxxx.git
注意,此时必需要保证须要下载的pod版本号和git仓库的tag标签号一致。全部依赖库下载以后,便进入了和Xcode工程的融合步骤。
在cocoaPods和Xcode工程进行集成的过程当中,会有有如下流程
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Demo/Demo.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
复制代码
create group 在工程中建立group文件夹,逻辑上隔离一些文件
create pod project & add pod library 建立pod.xcodeproject工程,而且将在podfile中定义的第三方库引入到这个工程之中。
add embed frameworks script phase 添加了[CP] Embed Pods Frameworks,相应的,多了pods_xxx的group,下列xxx.framework.sh,来完成将内部第三方库打包成.a静态库文件(在Podfile中若是选择了!use_frameworks,则此步骤会打包成.framework)
remove embed frameworks script phase 若是本次podfile删除了部分第三方库,则此步骤会删除掉不须要的第三方库,将其的引用关系从Pod.xcodeproject工程中拿走。
add copy resource script phase 若是第三方库存在资源bundle,则此步骤会将资源文件进行复制到集中的目录中,方便统一进行打包和封装。相应的,会添加[CP] Copy Pods Resources脚本。
add check manifest.lock script phase 前文提到过,manifest.lock实际上是podfile.lock的副本。此步骤会进行diff,若是存在不一致,则会提示著名的那句The sandbox is not sync with the podfile.lock
错误。
add user script phase 此步骤是对原有project工程文件进行改造。在运行过pod install
后,再次打开原有工程会发现没法编译经过,由于已经作了改动。
首先,添加了对Pod工程的依赖,具体为引用中多了libPods_xxx.a文件。此步骤的.a文件(或者.framework文件)为上述步骤中xxx.framework.sh打包出来的文件,也就是说,cocoaPods会把全部第三方的组件封装为一个.a文件(或者.framework文件)!
创建了Pods的group,内含pods-xxx-debug.xconfig和pods-xxx.release.xconfig文件。这两个文件是对应工程的build phase的配置。相应的,主工程的Iinfo->Configurations的debug和release配置会对应上述两个配置文件。
上述两个配置都作了什么?包括: Header_search_path,指向了Pod/Headers/public/xxx,添加了Pods文件编译后的头文件地址 Other_LDFLAGS,添加了-ObjC等等 一些Pods变了,例如Pods_BUILD_DIR等
至此,原有xcode工程和新建的Pod工程完成了集成和融合。
好了,cocoaPods的好处和原理已经介绍的差很少了。大部分时间,咱们经过引用github上的组件就够用了。可是有时候处于业务须要,咱们须要来实现私有Pod库。因此接下来咱们来介绍下如何在公司内网来实现一个私有库,实现一个私有组件。
所谓Spec Repo,就是Pods的索引。一旦在podfile中设置source为某个私有repo的git地址,在进行pod update的时候就会去这个repo中进行检索,若是检索到对应的pod,会读取该Pod的podspec从而进行安装。 一个Spec Repo的目录结构以下:
以后咱们去git.xxx.com上新建一个相应的Repo地址,以后添加repo到本地,该repo地址是为了后面提交podspec使用。
# pod repo add [Private Repo Name] [GitHub HTTPS clone URL]
pod repo add XXXCocoaPodsRepo git@git.xxx.com:XXX_SPA_XXX/iOS_CocoaPods_Repo.git
复制代码
成功后能够进入~/.cocoapods/repos
目录下查看XXXCocoaPodsRepo
这个目录了。
这里,咱们以HelloXXXPod为例。 去git.xxx.com上去新建项目,以后获取地址,为:
git@git.xxx.com:XXX_SPA_XXX/HelloXXXPod.git
复制代码
此时clone到本地,命令为:
git clone git@git.xxx.com:XXX_SPA_XXX/HelloXXXPod.git
复制代码
这里建议经过CocoPods的官方命令来进行Pod项目的建立,以测试项目HelloXXXPod为例,命令以下:
pod lib create HelloXXXPod
复制代码
不出意外地话,会提问你六个问题(cocoaPods v1.5.3版本):
1.What platform do you want to use? [ iOS / macOS ]
2.What language do you want to use? [ Swift / ObjC ]
3.Would you like to include a demo application with your library? [ Yes / No ]
4.Which testing frameworks will you use? [ Specta / Kiwi / None ]
5.Would you like to do view based testing? [ Yes / No ]
6.What is your class prefix?
复制代码
分别解释一下
What platform do you want to use?? [ iOS / macOS ] 问组件化应用在哪一个平台上,通常咱们选iOS
What language do you want to use? [ Swift / ObjC ] 使用何种语言,能够根据项目是OC仍是Swift自行选择
Would you like to include a demo application with your library? [ Yes / No ] 问是否须要一个Demo工程,方便调试Pod。若是是第一次作组件化,建议选Yes,方便pod的调试
Which testing frameworks will you use? [ Specta / Kiwi / None ] 问是否须要UT测试框架,可选择Specta和Kiwi,或者选择不要。
Specta是OC的一个轻量级TDD/BDD框架,参考github/specta
Kiwi是一个iOS的一个BDD框架,能够简单地部署和使用。github/kiwi UT测试框架若是要选择的话,建议选择Kiwi,能够参考我以前写的调研kiwi上手体验 本次的Demo,暂时选None
Would you like to do view based testing? [ Yes / No ] 若是上一步选择了Specta ,这步会生成一部分有利于作自动化测试的逻辑和代码
What is your class prefix? 这里能够指定你的项目前缀,这样在new一个类时会自动加上前缀
以后咱们运行pod install
,生成的文件目录树结构以下:
$ tree HelloXXXPod -L 2
HelloXXXPod
├── Example
│ ├── Build
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ ├── Tests
│ ├── helloXXXPod
│ ├── helloXXXPod.xcodeproj
│ └── helloXXXPod.xcworkspace
├── LICENSE
├── README.md
├── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
├── helloXXXPod
│ ├── Assets
│ └── Classes
└── helloXXXPod.podspec
复制代码
这时候能够在刚才生成的Example工程内作开发,这时候记得把新建的代码放到Classes目录下。若是有图片资源,建议放到Assets下。
开发、调试完成以后,就能够去编辑podspec文件了。按如下方式来修改,不明白的字段请参考官方文档:
这里给出本次Demo的podspec供各位参考:
Pod::Spec.new do |s|
s.name = 'helloXXXPod'
s.version = '0.1.0'
s.summary = 'A short description of helloXXXPod.'
s.description = <<-DESC
TODO: Add long description of the pod here.
DESC
s.homepage = 'https://git.xxx.com/XXX_SPA_XXX/HelloXXXPod'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'nimomeng' => 'nimomeng@tencent.com' }
s.source = { :git => 'git@git.xxx.com:XXX_SPA_XXX/HelloXXXPod.git', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.source_files = 'helloXXXPod/Classes/**/*'
end
复制代码
其中,注意修改这几个字段:
若是是经过pod lib create
命令建立的Pod,会在Example中自动配置好该pod的本地调试脚本,以下:
use_frameworks!
platform :ios, '8.0'
target 'helloXXXPod_Example' do
pod 'helloXXXPod' :path => '../'
target 'helloXXXPod_Tests' do
inherit! :search_paths
end
end
复制代码
其中,pod 'helloXXXPod' :path => '../'
的含义是说,在上层目录来下载helloXXXPod
这个pod。这是本地调试Pod的一种。 一样的,能够实现相似方式调试的方法,还有经过:podspec
命令来指定,指定pod所在的podspec文件位置便可
其中,path语法精确到目录便可;podspec语法必需要精确到文件。
设置好podfile以后,在Example文件下执行pod install
,则能够发现新的文件已经出如今项目工程的pods文件夹之下了。
注意,经过path
语法进行更新后,Pod中代码并不在Pod文件夹中,而是在一个叫 Development Pods
中。
开发完成,须要本地验证podspec,确保其有效:
pod lib lint helloXXXPod.podspec
复制代码
以后要作的就是把库同步到Git上去了。这时候须要去git.xxx.com上创建一个对应的仓库,例如:
http://git.xxx.com/XXX_SPA_XXX/HelloXXXPod.git (替换为本身的实际git地址)
复制代码
而后将代码同步到此Git上。
git add .
git commit -m "Init"
git remote add origin http://git.xxx.com/XXX_SPA_XXX/HelloXXXPod.git(替换为本身的实际git地址)
git push --set-upstream origin master
复制代码
podSpec文件须要版本控制信息,因此咱们要打一个Tag.
git tag -m "first demo" 0.1.0
git push --tags
复制代码
在执行本歩以前,确保最新代码已经提交到了Git上,且已经打好了tag.
向Spec Repo提交podspec的命令:
pod repo push XXXCocoaPodsRepo HelloXXXPod.podspec --allow-warnings
复制代码
在通过三轮的用户校验以后,提交成功!这时候咱们去~/.cocoapods/repos/XXXCocoaPodsRepo
中查看,咱们的的podspec已经在里面了!
此时经过pod search HelloXXXPod
已经能够查到了!
最后,为了保证本地的repo已经被更新,运行pod update
来更新repo
咱们能够在想要使用的项目中的Podfile里加入以下代码:
pod 'helloXXXPod'
复制代码
便可。 固然,因为咱们的是私有CocoaPods库,所以最好告诉系统这个库的source在哪里,所以在Podfile文件上部也请加上Spec Repo的git地址。同时,为了确保公共的cocoaPod能够被正常下载,请添加外部CocoaPod的库:
# For inner pods
source 'git@git.xxx.com:XXX_SPA_XXX/iOS_CocoaPods_Repo.git'
# For public pods
source 'https://github.com/CocoaPods/Specs.git'
复制代码
整个的Podfile文件看起来是这样的:
use_frameworks!
platform :ios, '8.0'
# source 'git@git.xxx.com:XXX_SPA_XXX/iOS_CocoaPods_Repo.git'
# For public pods
source 'https://github.com/CocoaPods/Specs.git'
target 'helloXXXPod_Example' do
pod 'helloXXXPod'
target 'helloXXXPod_Tests' do
inherit! :search_paths
end
end
复制代码
以后运行pod install
便可安装对应的Pods
咱们能够复用Example项目,只不过此次再也不经过:path
命令或者:podspec
命令来作本地调用,而是彻底使用安装外部pod的方式,即:
pod 'helloXXXPod'
复制代码
注意:虽然pod已经推送到线上,可是本地必定要先更新pod的repo,否则仍是没法找到最新的pod。确保先作pod update
操做。
Example项目中,咱们调用在Pod中写好的方法,查看是否输入对应的log便可验证:
至此,Pod建立完成。
若是pod中用到framework,应该在哪里添加?
若是pod中用到framework,如AVFoundation,直接在podspec文件中添加s.frameworks = ‘AVFoundation’或者s.frameworks = [‘AVFoundation’,'MapKit'],而不该该添加在项目的Link Binary With Libraries下面。
怎么取更新私有 pod?
更新私有pod的过程和建立pod的步骤一致,可是要记得在更改代码后要记得必定从新run一下aggregate,更改podspec里的s.version(由于tag不能重复提交), 从新pod repo push
若是出现这个错误怎么办:
[!] An unexpected version directory `Assets` was encountered for the `/Users/nimo/.cocoapods/repos/xxxx` Pod in the `xxxx` repository.
复制代码
这个错误,请查看: