工程健康治理

工程化

  • 定义: 为项目搭建一系列的周边工具,打通 CI / CD 流程
  • 解决问题: 解决标准操作流程(SOP 标准作业程序) 的问题

组件化

  • 定义:将项目打碎并拆分成若干个组件组成的项目,以面向组件的方式进行开发。
  • 解决的问题:解决工程业务复杂的问题。

组件化达到的效果是:

1. 化整为零,各自独立;确保每一部分是正确的,整体就是正确的。解决代码复用并是组件化要解决的主要问题,组件化要解决的问题是将复杂的大工程拆分成很多简单的小工程,且小工程之间能够互相协作;工程使用了 Cocoapods 也不意味着已完成组件化。组件化的是指代码可以独立编译,可以独立测试。
 
2. ## 好的架构没有 Common 没有 Core,也不应该有大组件的存在.

有 Common / Core 时,意味着有那么一部分代码,其职责是不明确的。不明确职责的代码会造成代码未来难以维护,因此不是一个好的架构。
另外每人对 Common / Core 的认知不同,很可能会使得 Common / Core 变成一个公共垃圾堆,出现 “既然放哪里都不合适,那我就放 Common / Core 吧” 的情况。该垃圾堆持续增长,成为一座垃圾山,导致 Common / Core 未来无法维护。

为什么不允许存在大组件

原因是大部分功能的开发,其实仅需要大组件的其中一个功能,但为了实现该功能,却需要引入一个大组件。这种情况不仅导致代码维护复杂,还会导致这个小组件编译时长不合理地增多。此时更合理的做法应该是将大组件打散成若干个小组件,其他组件需要啥,就只依赖它需要的那部分。我们认为一个大组件应该是由若干个小组件组成,而不是一个大组件由很多功能的代码拼成。每个组件实现了什么功能,就是什么组件。

组件化的实现方案解析:

1.基于预注册:分为 URL 注册 / Protocol 注册

目前注册类方案存在管理注册时序、大量注册实例造成无用内存消耗、大量注册代码造成的时间损耗等问题,对于这些问题,我们可以使用各种“补丁逻辑”(例如直接将注册 URL 注入 mach_O 文件等)来解决
  
2.基于中间件 Target - Action  Category(OC)和Extension(Swift) Runtime实现

组件化的实现可能会遇到的问题:

1.Argument List Too Long

Argument List Too Long 指的是 Xcode 代码编译基本完成后,在执行工程 Build Phases 中的 Run Script 时 / 编译时突然停止。

原因:Cocoapods 为 Pod 生成编译参数后,会写入到环境变量。此时若使用 Pod 构造的组件达到一定量级,会导致写入环境变量过多,继而导致环境变量总长度超过操作系统限制(即 26w 个字符),最终导致命令停止。

解决方案:环境变量中主要包含三个长度较长的内容,即 Header Search Path、Library Search Path 及 ModuleMap Path,所以关键是对这些信息进行合并。

1.合并 Header Search Path、Library Search Path 到一个文件夹:建立专用文件目录,将相关数据迁移至该专用目录,然后在 Xcode 中设定该目录。

2.合并 ModuleMap 到一个文件:需要注意的是,Xcode 要求我们提供 ModuleMap 文件路径(而非文件夹路径),所以这里关键在于如何合并 ModuleMap 文件。由于 ModuleMap 编译时才产生,所以得物在编译 Pre Action 时,将其他 ModuleMap 内容合并至当前 DerivedData 路径下的 ModuleMap,同时设定 Xcode 读取的 ModuleMap 文件路径。

容器化

  • 定义:利用拆分的组件,在快速满足业务需求的同时,尽量少地提升项目复杂度,同时以面向构件的方式进行开发。
  • 解决的问题:解决组件复用性的问题。

包体积大小治理

这两块内容主要有两款内容 ==资源== 和 ==代码==, 治理的方法主要就是这两块。

资源

  • 资源压缩

    • 给定资源无损压缩 (主要针对于美术给定资源未无损压缩的情况)
    • 80% 有损压缩 (此情况需要排除例外,例如部分特殊图片需要展示较高的效果,保留方式一的压缩方式)
    • 重复资源手动合并(初级)
    • 重复资源合并自动合并。利用脚本,在编译结束时,针对资源计算 MD5,然后确认相同MD5资源的数量,若存在多个,则移除相同的冗余资源,同时为了让大家都使用相同的资源,对 imageNamed: 等方法进行封装 / Hook,将传入的资源名字处理成 MD5,然后再进行资源的搜索。这样可以解决多组件使用相同资源的问题 ==(高级)==

  • 资源下发

    • 自建资源下发平台,每个版本对应一个资源包。eg:1.0.0 的版本,对应 1.0.0 的资源包;2.0.0 的版本,对应 2.0.0 的资源包。客户端通过“资源管理器”访问资源,如果资源没下载好,则访问 CDN 进行下载(非重要图片资源处理)。对于比较大的动画 / AR 资源,若未下载好,则使用图片资源进行业务逻辑的降级兜底。
    • 资源增量下发,只下载新版本新增资源。(对于资源下发平台,得物此块获得了大概 3 - 4 倍的提升)==(进阶)==

代码

  • 三方库裁剪

    • 针对只需要三方库一小部分功能的场景,对三方库进行定制化缩减,如移除不需要的功能
    • 移出类似三方库,指定使用某一个
    • 抽离稳定三方库细分功能做为组件
    • LinkMap解析可以让你看到各个库占的体积大小,定位到各个库的占用体积问题,从而提出缩减安装包大小的解决方案。

    分析工具 LinkMapParser


  • 代码治理

    • 无用代码扫描
    • 重复代码合并抽离成中间组件,各组件对中间组件进行依赖
    • 长依赖组件:部分项目中会存在组件依赖链过长的情况,如 A -> B -> C -> D(此处 "->" 代表依赖状态)。这样会导致使用 A 组件的时候,必须同时引入 B、C、D 组件。我们认为长依赖组件的出现是不合理的,是没有合理区分纵向依赖与横向依赖导致的。针对这部分内容的处理,需要调整依赖方式
      • 纵向依赖:直接依赖代码,通过 Pod Dependency 进行依赖。
      • 横向依赖:通过组件调度的方式进行间接依赖,无需直接依赖代码。
    • 长编译耗时组件:当新组件仅需使用大组件的小功能时,直接依赖了大组件,会导致新组件开发时,开发、编译成本变大。针对这部分内容,需要将大组件再次进行拆分成若干小组件进行处理。

  • Crash治理

    • Crash 管理系统(例如Bugly 等)手工确认 Crash 后分给具体的业务线,同时人工跟进问题修复情况。==(初级)==
    • 半自动化使用脚本爬 Bugly 信息(包括 Crash Stack 信息),然后自动识别业务线并自动落表。==(中级)==
    • 搭建自建 Crash平台,做自动化收集、解析、告警,同时实现自动跟进修复情况。==(高级)==

  • 启动流程治理

    • step1 启动流程分组:将代码按业务线分块并标注,用以确定每条业务线所占的时长,随后可根据该指标治理部分业务线大幅增长的不合理情况
    • step2 代码插桩diff:编译时,进行代码插桩。然后将新旧包代码插桩数据对比,当新增代码调用时,能够及时发现调用链的变化,用于确保调用链必要且合理。

  • 工程化

    • 围绕组件化的基础设施,需要注意的是,工程化的基础设施,需要随着组件化、容器化阶段不停调整。并不是意味进入组件化后,工程化就不用继续,而是进入组件化后,工作重点主要放在组件化,但仍需投入精力调整优化相关的基础设施。
      • 组件创建脚本、组件工具:解决创建、管理组件的问题。
      • 组件发版工具:解决组件发版的问题。
      • 二进制调试工具:解决二进制组件调试问题。
      • 组件上游依赖查询工具:解决组件依赖查询问题。
      • 编译成功节点切换工具:解决“出包难”的问题。
      • 裙带源码组件切换工具:解决 ARC 不对齐的问题

  • 组件管理

    在 Podfile 中== 不使用 == 定义版本号的方式,采用索引库的方式来解决组件频繁更新的问题。根据不同的开发环境建立不同的索引库,基于上述考虑,前主要使用索引做组件管理,且固定以下命名规范:
    [图片上传失败...(image-2a3667-1648176146458)]

    • 开发环境:使用 dev 索引库,规定组件发布新版本时,仅变更版本号的第一位,就像 A,从 1.0.0 变为 2.0.0。
    • 沙盒环境:使用 test 索引库,规定组件发布新版本时,仅变更版本号的第二位,就像 B,从 1.0.0 变为 1.1.0。
    • 灰度环境:使用 gray 索引库,规定组件发布新版本时,仅变更版本号的第三位,就像 C,从 1.0.0变为 1.0.1。
    • 现网环境:使用 release 索引库,规定组件不能发布新版本。

    不同环境使用不同的 Git 仓库,且 Podfile 中固定了每个组件当前环境的仓库地址(所以切换环境时,需要调整 Podfile 中的 Source,并处理不同环境 git upstream 的合并)。仓库环境的拆分,避免了新版本开发影响到已有版本逻辑。


  • 二进制

    • 单工程独立编译制作

      • 优点:组件发版时完成二进制制作;可靠性高。
      • 缺点:组件制作二进制时长取决于组件规模,大组件制作仍旧耗时;存在大量组件需要做二进制,单光算编译时长,1400个组件 需要 5 天制作;出现测试包体积变大。
    • 全工程编译产物制作:利用编译缓存,从 Xcode 编译缓存 DerivedData 中取出组件

      • 优点:每 50min 完成 1400+ 二进制制作。
      • 缺点:只能分批制作(1h / 次);存在 ARC 不对齐的情况。
      • ARC 不对齐:A 组件已经是二进制(在编译时加入 ARC),B 组件使用源码编译,此时再次编译(编译器会为 B 会添加 ARC,但不会对 A 组件进行处理,有可能导致 ARC 不对齐引发内存被提前释放,导致 EXC_BAD_ACCESS Crash)。为了解决该问题,在做二进制时,需要对新发版的组件做裙带组件源码切换:将其上游一级、下游一级的组件变成源码依赖而非二进制依赖
      • 增量制作: 将一个组件的上一级和下一级依赖的组件变为源码参与编译,解决ARC 不对齐问题

  • 持续交付

    • Xcode Server 定时打包 :CI 机定时打包

      • 能够避免编译错误长时间不被解决的问题。
      • 不保证编译成功,且每轮编译时长较长,相隔 1h 才能获得编译结果;但 CI 机编译成功不代表开发端编译一定能成功
    • 记录编译成功节点并提供脚本用以切换到该分支

      • 优点:能够避免编译错误长时间不被解决的问题;能够最大限度保证开发端代码编译成功。
      • 缺点:编译成功节点落后近 1h;由于开发的组件一定是源码,若使用二进制编译,会造成 ARC 不对齐引起的各种偶发问题。
    • 多机车轮打包: 使用大量机器持续构建不同环境的包

      • 优点:能够及时发现编译错误,每轮 10 min;能够最大限度保证开发端代码编译成功。
      • 缺点:需要较多机器资源。
    • 构建失败处理

      • 定期输出构建信息, 如遇到失败,则自动提醒引发失败的工程师(提交者)。
      • 标记处理状态。

--- 整理至得物 得物iOS工程演进之路

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容