iOSPlayground.xcodeproj | 壳工程,包含 App Target:iOSPlayground | ||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
iOSPlayground | 壳工程文件目录,包含资源、代码、Info.plist | ||||||||||||||||||||||||||||||||||||||
Podfile | 声明 User Target 的依赖 | ||||||||||||||||||||||||||||||||||||||
Gemfile | 声明 CocoaPods 的版本,这里是 1.7.5"}}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们在 Podfile 中为 Target「iOSPlayground」引入 SDWebImage 以及 SDWebImage 的两个 Coder,并声明这些组件的版本约束"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"platform :ios, '11.0'\nproject 'iOSPlayground.xcodeproj'\ntarget 'iOSPlayground' do\n pod 'SDWebImage', '~> 5.6.0'\n pod 'SDWebImageLottieCoder', '~> 0.1.0'\n pod 'SDWebImageWebPCoder', '~> 0.6.1'\nend\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后执行 Pod install 命令 "},{"type":"codeinline","content":[{"type":"text","text":"bundle exec pod install"}]},{"type":"text","text":",CocoaPods 开始为你构建多依赖的开发环境;整个 Pod Install 流程最核心的就是 "},{"type":"codeinline","content":[{"type":"text","text":"::Pod::Installer"}]},{"type":"text","text":" 类,Pod Install 命令会初始化并配置 Installer,然后执行 install! 流程,install! 流程主要包括 6 个环节"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/09\/090c4ec86bd7ba3e1fc9d4f87e5deee8.png","alt":"图片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"def install!\n prepare\n resolve_dependencies # 依赖决议\n download_dependencies # 依赖下载\n validate_targets # Pods 校验\n generate_pods_project # Pods Project 生成\n if installation_options.integrate_targets?\n integrate_user_project # User Project 整合\n else\n UI.section 'Skipping User Project Integration'\n end\n perform_post_install_actions # 收尾\nend\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面会对这 5 个流程做一些简单分析,为了简单起见,我们会忽略一些细节。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"准备阶段"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个流程主要是在 Pod Install 前做一些环境检查,并且初始化 Pod Install 的执行环境。"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/78\/786a85db421a8212cf846b93d610893a.png","alt":"图片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"依赖分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个流程的主要目标是分析决议出所有依赖的版本,这里的依赖包括 Podfile 中引入的依赖,以及依赖本身引入的依赖,为 Downloader 和 Generator 流程做准备。"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/02\/027cc9f00a195fb330722934da446c5c.png","alt":"图片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个过程的核心是构建 Molinillo 决议的环境:准备好 Specs 仓库,分析 Podfile 和 Podfile.lock,然后进行 Molinillo 决议,决议过程是基于 DAG(有向无环图)的,可以参考下图,按照最优顺序依次进行决议直到最后决议出所有节点上依赖的版本和来源。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/f7\/f7972a700bb8f7b126e2d059b8a3beba.gif","alt":"图片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"iOSPlayground 工程最后决议出的依赖列表是:"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/20\/209d1ba49b70a7122c010c6ef0d719aa.png","alt":"图片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基于最后决议的结果我们就可以获取 Specifications、生成 Aggregate Targets 和 Pod Targets。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Aggregate Targets:"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/a0\/a04f1485c9fe25ae4c3d34afa6837376.png","alt":"图片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Pod Targets:"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/7f\/7feb5200211900c251ee6f9e280e6fcd.png","alt":"图片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"embedcomp","attrs":{"type":"table","data":{"content":"
|
抖音研发效能建设 - CocoaPods 优化实践
{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"背景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"抖音很早就接入 CocoaPods 进行依赖管理了,项目前期抖音只有几十个组件,业务代码也基本在壳工程内,CocoaPods 可以满足业务研发的需求,但是随着业务的不断迭代,代码急剧膨胀,同时抖音工程也在进行架构优化,比如工程组件化改造,组件的数量和复杂度不断增加:组件(Pod)数量增加到 400+ ,子组件(Subspec)数量增加到 1500+ ,部分复杂组件的描述文件(podspec)膨胀到 1000+ 行,这导致了依赖管理流程(主要是 Pod Install)的效率不断下降,同时也导致了 Xcode 检索和构建效率下降。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了效率下降外,我们也开始遇到一些 CocoaPods 潜在的稳定性问题,比如在 CI\/CD 任务并发执行的环境下 Pod Install 出现大量失败,这些问题已经严重影响了我们的研发效率。在超大工程、复杂依赖、快速迭代的背景下,CocoaPods 已经不能很好地支撑我们的研发流程了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"反馈最多就是 Pod Install 慢,经常会有同学反馈 Pod Install 流程慢,涉及到决议流程慢,依赖下载慢、Pods 工程生成慢等"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"本地 Source 仓库没更新,经常导致找不到 Specification,Pod Install 失败"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"依赖组件多,循环依赖报错,但是难以找到循环链路"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"依赖组件多,User 工程复杂度,导致 Pod Install 后 Xcode 工程索引慢,卡顿严重"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"依赖组件多,工程构建出现不符合预期的失败问题,比如 Arguments Too Long"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"研发流程上,有部分研发同学本地误清理了 CocoaPods 缓存,导致工程编译或者链接失败"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","text":"组件拆分后,新添加文件必须 Pod Install 后才可以被其他组件访问,这拖慢了研发效率"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们开始尝试在 0 侵入、不影响现有研发流程的前提下,改造 CocoaPods 做来解决我们遇到的问题,并且取得了一些收益。在介绍我们的优化前,我们会先对 CocoaPods 做一些介绍, 我们以 CocoaPods 1.7.5 为例来做说明依赖管理的核心流程「Pod Install」"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Pod Install"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们以一个 MVP 工程「iOSPlayground」为例子来说明,iOSPlayground 工程是怎么组织的:"}]},{"type":"embedcomp","attrs":{"type":"table","data":{"content":"
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.