从基础概念到实战应用,系统梳理 iOS 架构演进之路
一、基础概念 1.1 为什么需要架构优化? 随着业务迭代,单体 App 会遇到诸多问题:
问题
表现
影响
代码耦合严重
模块间直接 import,循环依赖
难以维护、编译慢
编译效率低
改一行代码全量编译
开发效率下降
团队协作冲突
多人修改同一工程
Git 冲突频繁
无法独立开发
强依赖主工程
无法并行开发、独立测试
复用困难
业务逻辑与 UI 混在一起
跨项目复用成本高
1.2 三种架构模式辨析 1 2 3 4 5 6 7 8 9 10 11 12 ┌─────────────────────────────────────────────────────────────────┐ │ 架构演进路径 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 单体架构 ──► 模块化 ──► 组件化 ──► 插件化 │ │ │ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ 按功能拆分 物理隔离 独立仓库 运行时动态加载 │ │ 代码目录 组件解耦 CocoaPods 热更新/按需加载 │ │ │ └─────────────────────────────────────────────────────────────────┘
模块化(Modularization)
定义 :按业务功能将代码拆分为独立的「模块」,每个模块有清晰的边界
特点 :逻辑划分、职责单一,通常仍在同一工程内
粒度 :中等,如「用户模块」「订单模块」「支付模块」
组件化(Componentization)
定义 :将模块进一步拆分为可独立编译、可复用的「组件」,通过依赖注入解耦
特点 :物理隔离、独立仓库、独立编译、协议解耦
粒度 : finer,如「登录组件」「分享组件」「埋点组件」
插件化(Pluginization)
定义 :组件可动态加载、热更新,主 App 与插件解耦到运行时
特点 :运行时动态、按需加载、可热修复
粒度 :动态 Bundle/Framework
二、模块化原理与实践 2.1 模块化的核心原则
单一职责 :每个模块只负责一块业务
接口隔离 :模块间通过 Protocol 通信,不暴露实现细节
依赖倒置 :依赖抽象(协议)而非具体实现
2.2 目录结构示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 MyApp/ ├── App/ # 主工程壳 │ ├── AppDelegate │ └── Main ├── Modules/ │ ├── ModuleA_User/ # 用户模块 │ │ ├── Services/ │ │ ├── Views/ │ │ └── Models/ │ ├── ModuleB_Order/ # 订单模块 │ └── ModuleC_Payment/ # 支付模块 ├── Common/ # 公共基础库 │ ├── Network/ │ ├── Utils/ │ └── BaseClasses/ └── Router/ # 路由层(模块间通信枢纽)
2.3 模块间通信:Protocol + 依赖注入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 protocol UserServiceProtocol : AnyObject { func getCurrentUser () -> User ? func logout () } class UserService : UserServiceProtocol { func getCurrentUser () -> User ? { } func logout () { } } class ServiceLocator { static let shared = ServiceLocator () var userService: UserServiceProtocol ? } class OrderViewController : UIViewController { var userService: UserServiceProtocol ? func showUserInfo () { guard let user = userService? .getCurrentUser() else { return } } }
三、组件化原理与实现 3.1 组件化架构图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ┌──────────────────┐ │ App Shell │ │ (主工程/壳工程) │ └────────┬─────────┘ │ ┌──────────────┼──────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 用户组件 │ │ 订单组件 │ │ 支付组件 │ │ (Pod) │ │ (Pod) │ │ (Pod) │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ └───────────────┼───────────────┘ │ ┌──────▼──────┐ │ 路由/中间层 │ │ (URL/Mediator)│ └──────┬──────┘ │ ┌──────▼──────┐ │ 基础组件 │ │ (网络/缓存/UI)│ └─────────────┘
3.2 路由方案对比
方案
原理
优点
缺点
URL Router
字符串 URL 映射到 VC
简单、支持 H5 跳转
参数传递不便、类型不安全
Target-Action (Mediator)
通过 Mediator 类反射调用
解耦彻底、类型安全
需维护中间类
Protocol
协议注册 + 实现查找
接口清晰
需集中注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 import UIKitpublic class CTMediator : NSObject { public static let shared = CTMediator () private var targetCache = [String : NSObject ]() public func perform (target targetName : String , action actionName : String , params : [String : Any ]? = nil ) -> Any ? { let targetClassString = "Target_\(targetName) " let actionString = "Action_\(actionName) :" guard let targetClass = NSClassFromString (targetClassString) as? NSObject .Type else { return nil } var target = targetCache[targetClassString] if target == nil { target = targetClass.init () targetCache[targetClassString] = target } let selector = NSSelectorFromString (actionString) guard (target? .responds(to: selector)) ?? false else { return nil } return target? .perform(selector, with: params)? .takeUnretainedValue() } } class Target_Order : NSObject { @objc func Action_orderListViewController (_ params : [String : Any ]? ) -> UIViewController { let userId = params? ["userId" ] as? String ?? "" let vc = OrderListViewController (userId: userId) return vc } } let vc = CTMediator .shared.perform( target: "Order" , action: "orderListViewController" , params: ["userId" : "12345" ] ) as? UIViewController navigationController? .pushViewController(vc! , animated: true )
3.4 依赖关系设计 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 target 'MainApp' do pod 'UserModule' pod 'OrderModule' pod 'PaymentModule' pod 'Mediator' pod 'Mediator/Order' pod 'Mediator/User' end Pod::Spec .new do |s | s.name = 'OrderModule' s.dependency 'Mediator' s.dependency 'BaseNetwork' s.dependency 'BaseUI' end
关键点 :业务组件之间不直接依赖,都通过 Mediator 间接通信。
四、插件化原理与实现 4.1 插件化的应用场景
热更新 :修复线上 Bug 无需发版
按需加载 :减少包体积,冷启动只加载核心
动态能力 :运营活动插件、A/B 测试模块
多端复用 :同一套插件可被主 App、Widget、Watch 加载
4.2 iOS 插件化技术选型
技术
说明
限制
Dynamic Framework
动态库,可延迟加载
App Store 限制主二进制外的动态库
Bundle + 反射
资源/代码打包成 .bundle,运行时加载
无法绕过沙盒,需提前打包进 App
JavaScript 引擎
React Native / Flutter / JSI
非原生,性能与体验有差异
JSPatch / 热修复
通过 Runtime 动态替换方法
已不可上架 App Store
注意 :苹果审核禁止下载执行任意代码,真正「从网络下载插件并执行」的纯插件化在 App Store 场景不可行。实际做法多为:预置多个 Framework/Bundle,运行时按需加载 ,或通过 JS 引擎 + 离线包 实现业务热更新。
4.3 动态 Framework 加载示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import UIKitclass PluginManager { static let shared = PluginManager () private var loadedBundles: [String : Bundle ] = [:] func loadPlugin (named pluginName : String ) -> Bool { guard let path = Bundle .main.path( forResource: pluginName, ofType: "framework" , inDirectory: "Frameworks" ) else { return false } guard let bundle = Bundle (path: path), bundle.load() else { return false } loadedBundles[pluginName] = bundle return true } func instantiateViewController (fromPlugin pluginName : String , className : String ) -> UIViewController ? { guard let bundle = loadedBundles[pluginName] ?? { loadPlugin(named: pluginName) ? loadedBundles[pluginName] : nil }() else { return nil } guard let pluginClass = bundle.classNamed(className) as? UIViewController .Type else { return nil } return pluginClass.init () } } if PluginManager .shared.loadPlugin(named: "ActivityPlugin" ) { let vc = PluginManager .shared.instantiateViewController( fromPlugin: "ActivityPlugin" , className: "ActivityPlugin.ActivityViewController" ) present(vc! , animated: true ) }
4.4 基于 Runtime 的模块注册 1 2 3 4 5 6 7 8 9 10 11 12 + (void )load { [[PluginRegistry shared] registerPlugin:@"Activity" withClass:[ActivityPlugin class ]]; } @interface PluginRegistry : NSObject - (void )registerPlugin:(NSString *)name withClass:(Class)cls; - (id )createInstanceForPlugin:(NSString *)name; @end
五、BeeHive 框架源码解析 BeeHive 是阿里开源的 iOS 模块化框架,结合了 Protocol 注册 与 Module 生命周期 。
5.1 架构概览 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ┌────────────────────────────────────────────────────────┐ │ BHContext (上下文) │ │ - 配置信息、环境变量、共享数据 │ └────────────────────────────────────────────────────────┘ │ ┌─────────────────────────┴─────────────────────────────┐ │ BHModuleManager │ │ - 管理 Module 注册、加载、生命周期 │ └────────────────────────────────────────────────────────┘ │ ┌─────────────────┼─────────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ BHService │ │ BHModule │ │ BHConfig │ │ (协议-实现) │ │ (模块基类) │ │ (配置) │ └──────────────┘ └──────────────┘ └──────────────┘
5.2 核心类:BHModuleManager 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @implementation BHModuleManager - (void )registerDynamicModule:(Class)moduleClass { if ([moduleClass conformsToProtocol:@protocol (BHModuleProtocol )]) { BHModuleInfo *info = [[BHModuleInfo alloc] initWithModuleClass:moduleClass]; [self .modules addObject:info]; [self .modules sortUsingComparator:^NSComparisonResult (BHModuleInfo *m1, BHModuleInfo *m2) { return m1.priority < m2.priority; }]; } } - (void )triggerEvent:(NSInteger )eventType { for (BHModuleInfo *info in self .modules) { id <BHModuleProtocol> instance = [info moduleInstance]; switch (eventType) { case BHMSetupEvent: [instance modSetUp:nil ]; break ; case BHMInitEvent: [instance modInit:nil ]; break ; case BHMSplashEvent: [instance modSplash:nil ]; break ; } } } @end
5.3 Protocol 注册与查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - (void )registerService:(Protocol *)protocol implClass:(Class)implClass { NSString *key = NSStringFromProtocol (protocol); self .services[key] = implClass; } - (id )createService:(Protocol *)protocol { Class implClass = self .services[NSStringFromProtocol (protocol)]; return [[implClass alloc] init]; } @protocol UserServiceProtocol <NSObject >- (User *)currentUser; @end [[BHServiceManager sharedManager] registerService:@protocol (UserServiceProtocol ) implClass:[UserServiceImpl class ]]; id <UserServiceProtocol> service = [[BHServiceManager sharedManager] createService:@protocol (UserServiceProtocol )];
六、实际项目应用案例 6.1 某电商 App 组件化拆分 1 2 3 4 5 6 7 8 9 10 11 MainApp (壳工程) ├── HomeModule # 首页 ├── ProductModule # 商品详情 ├── CartModule # 购物车 ├── OrderModule # 订单 ├── UserModule # 用户中心 ├── PaymentModule # 支付 ├── ShareModule # 分享(可复用) ├── AnalyticsModule # 埋点(可复用) ├── Mediator # 路由中间层 └── BasePods # 网络/缓存/UI 基础库
收益 :
编译时间:全量 8min → 单模块 1.5min
4 个业务线可并行开发,Git 冲突减少 70%
ShareModule、AnalyticsModule 复用到多个 App
6.2 路由设计实战 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 extension CTMediator { func orderListViewController (userId : String ) -> UIViewController ? { return perform(target: "Order" , action: "orderListViewController" , params: ["userId" : userId]) as? UIViewController } func orderDetailViewController (orderId : String ) -> UIViewController ? { return perform(target: "Order" , action: "orderDetailViewController" , params: ["orderId" : orderId]) as? UIViewController } } if let vc = CTMediator .shared.orderListViewController(userId: "123" ) { navigationController? .pushViewController(vc, animated: true ) }
6.3 解耦实践:避免循环依赖 错误示例 :
1 2 OrderModule -> UserModule (获取用户信息) UserModule -> OrderModule (跳转订单列表)
形成循环依赖,无法独立编译。
正确做法 :
1 2 OrderModule -> Mediator UserModule -> Mediator
需要「用户信息」时,Order 通过 Mediator 获取 UserServiceProtocol;需要「跳转订单」时,User 通过 Mediator 获取 OrderListViewController。Mediator 依赖各模块的 Target 类(可通过 Category 按需引入)。
七、最佳实践与注意事项 7.1 模块拆分原则
高内聚低耦合 :模块内聚度高,模块间依赖少
按业务边界拆分 :参考 DDD 的 Bounded Context
基础组件下沉 :网络、缓存、日志等抽成独立 Pod
渐进式演进 :先模块化再组件化,避免一步到位导致成本过高
7.2 常见坑与规避
问题
原因
规避
编译顺序错误
组件间隐式依赖
严格检查 Pod 依赖,用 pod lib lint 校验
协议爆炸
过度抽象
只对跨模块通信定义协议,模块内部保持自由
路由表膨胀
所有页面都走路由
仅对外暴露的页面注册路由
启动变慢
Module 过多,+load 耗时
延迟注册、按需加载、控制 Module 数量
7.3 架构演进路线图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Phase 1: 模块化 ├── 按功能拆分目录 ├── 引入 Protocol 解耦 └── 建立 ServiceLocator Phase 2: 组件化 ├── 拆分为独立 Pod ├── 引入 Mediator/路由 └── 独立编译、独立开发 Phase 3: 插件化(可选) ├── 非核心模块动态加载 ├── 预置多套业务 Bundle └── 或引入 RN/Flutter 做动态化
八、总结
架构
适用场景
核心手段
模块化
中小型 App、团队 < 10 人
目录拆分、Protocol、依赖注入
组件化
中大型 App、多业务线并行
独立 Pod、Mediator、路由
插件化
需要动态化、热更新
动态 Framework、Bundle、JS 引擎
架构没有银弹,需结合团队规模、业务复杂度、迭代节奏选择合适方案。建议从模块化起步,随着复杂度提升再逐步演进到组件化,插件化则按实际需求谨慎引入。
参考资源