iOS组件化的一些思考

组件化的一些思考

1. 组件化之前

1.1 组件化的目标

  • 依赖解耦,模块化服务, 提升代码复用率
  • 独立分库,二进制打包,提升编译速度
  • 提升大团队、跨团队的开发效率

1.2 如何组件拆分

面临主要问题:

  1. 主业务库依赖复杂,不易拆分模块。
  2. 模块之间的依赖关系复杂·,很多模块存在不合理的横向依赖。
  3. 业务模块的边界是什么,粒度大小。

拆分模块的原则:

  1. 由下到上,单向依赖:
  2. 由粗到细:
  3. 由新到旧 :
  4. 模块间遵守接口隔离原则:
  5. 模块内遵守控制反转原则

参考模型:

image.png

App壳工程: 负责管理各个业务组件和打包APK,没有具体的业务功能。
业务组件层: 根据不同的业务构成独立的业务组件,其中每个业务组件包含一个Export Module和Implement Module。
功能组件/核心服务层: 对上层提供基础功能服务,如相机服务、IM、播放器、视频等。
组件基础设施: 包括Router提供页面路由服务、Kits、 SDK等。

2 组件管理

2.1. 组件工程标准化

创建统一的一套动态库/静态库工程模板,通过脚本新建静态库/动态库

  • 完成统一基本的podfile配置
  • 完成统一基本的podspec配置
  • 统一xcodeproject的配置统一:Enable bitcode设置为NO,Mach-O type设置为Static Library等
  • 目标framework target
运行 sh TBTemplate.sh TBNewFramework 你要生成工程的名称
以MyFramework为例
sh TBTemplate.sh TBNewFramework MyFramework
image.png
  1. 你可以把自己模块依赖的库写在podspec中,编译构建自动同步到在podfile里。
  2. 将你原来的源码文件拖入到目标framework target里,在这里就是MyFramework这个target,效果如下


    image.png

2.2. 组件管理平台化

通过CI平台创建组件模块,完成depoy配置(支持打包源码和静态库), 编译构建、持续集成等。下图是MTL平台


image.png

参考下滴滴的组件管理工具:


image.png

2.3. 组件开发规范化

命名规范

  • Module :包含界面且实现某部分具体业务功能的模块叫 Module。
  • Service :只包含具体业务逻辑的模块叫 Service。
  • SDK :不包含业务逻辑,提供基础服务的组件叫 SDK。
  • Kit :包含多个类似基础服务的组件叫 Kit。
    规范头文件的使用: 命名、设置可见性、头文件导入规范
    规范podspec的管理 : 检查podspec中的dependency和podfile中引入的依赖是否一致。podfile有依赖变更,必须同步到podspec。
    验收:确保framework工程本地编译通过,以及MTL平台打包成功
    回归和Review:按照TestCase进行功能回归,打点回归,对模块变更进行review。

2.4. 组件的开发测试

使用脚本或工具将模块的源码映射成pod的开发库, 在主工程进行修改调试。

3 集成壳工程

  • 壳工程提供基础的依赖和功能,提供独立的运行能力,
  • 管理初始化、启动流程、主页面多tab的UI框架

4 组件通讯

iOS 组件化的三种方案
OC底层知识点之-组件化(下)组件化通信

4.1. URL路由

自己开发Router或开源MGJRouter等:使用url-block方案
约定URI规则:fnpt://module/vc/pushOrPresent/?key=value&key1=value

优点:

  • 实现简单,易于理解和使用;
  • 方便地统一管理多平台的路由规则
  • 易于适配 URL Scheme

缺点:

  • 传参方式有限,并且无法利用编译器进行参数类型检查,因此所有的参数都只能从字符串中转换而来
  • 只适用于界面模块,不适用于通用模块
  • 需要手动维护路由表,容易出现冗余和错误;
  • 不能使用 designated initializer 声明必需参数

4.2. Target - Action

iOS组件化第二步:使用中间者模式(CTMediator)
CTMediator 是一个中间人模式(Mediator Pattern)的实现,用于 iOS 组件化开发中的模块间通信方案。
优点:

  • 实现方式轻量级, 调用时区分了本地应用调用和远程应用调用。本地应用调用为远程应用调用提供服务。
  • 组件仅通过Action暴露可调用接口,模块与模块之间的接口被固化在了Target-Action这一层,避免了实施组件化的改造过程中,对Business的侵入,同时也提高了组件化接口的可维护性。
  • 方便传递各种类型的参数。
  • 现在也支持swfit了。 CTMediator的Swift应用

如果你的工程是采用CTMediator方案做的组件化,看完本文以后,你就可以做到渐进式地迁移到Swift了。 CTMediator支持所有情况的调用,具体可以看文后总结。你的工程可以让Swift组件和Objective-C组件通过CTMediator混合调用 也就是说:以后再开新的组件,可以直接用Swift来写,旧有代码不会收到任何影响。

缺点:

  • 需要在mediator和target中重新添加每一个接口,模块化时代码较为繁琐
  • 在category中仍然要引入字符串硬编码,内部使用字典传参,一定程度上也存在和URL路由相同的问题
  • 无法保证使用的模块一定存在,target在修改后,使用者只能在运行时才能发现错误
  • 创建过多的target类,导致target类泛滥

4.3. Protocol - Class

protocol匹配的实现思路是:

  1. 将protocol和对应的类进行字典匹配
  2. 通过用protocol获取class,再动态创建实例 protocol比较典型的三方框架就是阿里的BeeHive。BeeHive借鉴了Spring Service、Apache DSO的架构理念,采用AOP+扩展App生命周期API形式,将业务功能、基础功能模块方式以解决大型应用中的复杂问题,并让模块之间以Service形式调用,将复杂问题切分,以AOP方式模块化服务

优点:
1、利用接口调用,实现了参数传递时的类型安全
2、直接使用模块的protocol接口,无需再重复封装

缺点:
1、用框架来创建所有对象,创建方式不同,即不支持外部传入参数
2、用OC runtime创建对象,不支持swift
3、只做了protocol 和 class 的匹配,不支持更复杂的创建方式 和依赖注入
4、无法保证所使用的protocol 一定存在对应的模块,也无法直接判断某个protocol是否能用于获取模块

URL Router、Target-Action 和依赖注入都是 iOS 组件化的常见方案。
URL Router 是一种通过定义一系列 URL Scheme,将各个组件对应到不同的 URL 上,然后在应用中注册路由表,在接收到特定 URL 时,根据路由表将请求转发到相应的组件中的方案。这种方案实现简单,易于理解和使用,但需要手动维护路由表,存在冗余和错误的风险。
Target-Action 是一种通过在编译期间生成各个组件之间的依赖关系,然后再通过 Runtime 动态加载组件,实现模块之间解耦的方案。这种方案可以保证类型安全,并且可以使用反射机制进行运行时注入,但需要手动处理每个模块之间的依赖关系,对于多层嵌套的依赖关系使用起来比较繁琐。
依赖注入是一种通过协议和遵循者实现依赖注入的方案。将各个组件之间需要使用的接口抽象出来,定义成协议,然后在应用启动时,通过注册表将不同的实例与相应的协议对应起来。这种方案可以解决组件之间依赖关系的问题,但过度依赖 DI 也会增加代码复杂度和维护成本。
从性能,可读性,功能性,代码量,规范约束 几个维度综合考虑我建议
CTMediator > 依赖注入 > URL-Router

当然选择哪种方案取决于具体的项目需求和开发团队的技术栈,需要权衡各种因素,选择最适合自己的方案,以上仅代表个人观点。

小结

模块的拆分需要划定好边界,梳理好依赖,做好模块解耦
组件的制作标准化,规范化,组件的集成平台化。
项目的平台化管理,包括项目的创建、构建,集成、回归、发布、hotfix、配置等等。

参考文章

百度App iOS工程化实践: EasyBox破冰之旅
百度 App 组件化之路
我所理解的组件化之路
Android组件化方案及组件消息总线modular-event实战
火掌柜 iOS 端基于 CocoaPods 的组件二进制化实践

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

推荐阅读更多精彩内容