进行 iOS 开发时,总有一天会出现这样的问题。
“这个直接通过构造器传入就行,真的有必要连 DI 容器也用上吗?”
随着 side project 变大,每个人都会遇到一次这样的烦恼。我在实际项目中都使用过 Swinject 和 Factory,并据此建立了判断标准。
先说结论。
如果页面少于 20 个,只用构造器注入、不使用 DI 容器就足够了。
当依赖关系图变复杂、测试替身需要频繁替换时,就是引入的时机。
如果要引入,选择 Factory。
我会按照自己的经历,说明什么时候引入,以及为什么选择 Factory。
引入 DI 容器前,请先确认这些事项
先整理最容易混淆的部分。
依赖注入(DI)和 DI 容器并不是一回事。
依赖注入是从外部提供所需对象的设计习惯,完全不需要库。
// 这也已经是很好的依赖注入
final class LoginViewModel {
private let authService: AuthService
init(authService: AuthService) {
self.authService = authService
}
}
而 DI 容器则是在一个地方统一管理这些对象在哪里、如何创建的工具。
如果只靠构造器注入就够了,现在引入容器还太早。
我用下面这些信号判断是否该引入。
- 初始化代码在每个页面重复,开始觉得传递对象很麻烦时
- 测试中经常需要替换成伪对象时
- 应用全局需要让多个地方共享同一个实例(具有单例性质)时
- 团队变大,经常问“这个对象在哪里创建?”时
如果其中两项以上符合,就可以开始考虑引入容器。
Swinject 和 Factory 有什么区别?
Swinject 是 Swift 生态中自 2015 年起使用的传统运行时容器。它注册到 Container,再通过 resolve 取出。问题在于,resolve 会返回可选值。忘记注册时,编译仍能正常通过,直到运行应用并在该页面强制解包 nil 时才会崩溃。
Factory 正是为弥补这类传统容器的缺点而诞生的项目。创建 Resolver 的 Michael Long 结合这段经验重新设计了它。核心理念是“定义即注册”。由于工厂定义为容器的属性,根本不存在漏注册的问题。错误引用会在编译时而不是运行时产生错误。
下面整理了截至 2026 年我感受到的差异。
| 项目 | Swinject | Factory |
|---|---|---|
| 注册与解析方式 | 运行时 resolve(返回可选值) | 基于类型的定义 + 属性包装器 |
| 漏注册时 | 运行时 nil/崩溃 | 通过编译错误提前拦截 |
| 作用域管理 | 支持 | 支持(.singleton、.cached、.shared 等) |
| 外部依赖 | 依赖独立框架 | 轻量单一包 |
| 学习曲线 | 略陡 | 比较平缓 |
过去有人建议“需要复杂作用域管理时就用 Swinject”,但现在已经不是这样了。Factory 同样完整支持单例、缓存和共享作用域。Swinject 能做的事,Factory 可以更安全地完成。
所以结论是 Factory
我实际判断时采用的标准如下。
如果现在引入,Factory 一个就够了
无论是个人项目还是团队项目,Factory 都能在没有外部依赖的情况下从一个包开始,因此负担很小。最关键的是,因忘记注册而在运行时崩溃的事故从结构上消失了。
// Factory: 定义即注册
extension Container {
var authService: Factory<AuthService> {
self { LiveAuthService() }.singleton // 作用域也只需一行
}
}
// 使用方
@Injected(\.authService) private var authService
在测试中替换成 mock 也只需一行。
Container.shared.authService.register { MockAuthService() }
什么时候保留 Swinject?
维护已经使用 Swinject 构建的大型代码库时。不必立即移除运行良好的 assembly 结构。不过,即使在这类项目中,也有越来越多团队从新模块开始逐步迁移到 Factory。新项目采用时,现在很难找到选择 Swinject 的理由。
常见问题(Q&A)
Q. 不能从一开始就使用容器吗?
不反对,但不推荐。依赖关系图简单时,容器反而会多隐藏一层代码,让追踪变得困难。
Q. 可以从 Swinject 迁移到 Factory 吗?
可以。让两个容器暂时共存,再按模块迁移,是比较现实的渐进式迁移方式。由于需要重新调整注册位置和作用域设置,大型项目会花些时间,但迁移后就不用担心运行时崩溃了。
Q. SwiftUI 项目选哪个更好?
当然是 Factory。它基于属性包装器,能自然融入 SwiftUI 的声明式风格,在预览中插入 mock 也很简单。
DI 容器在真正需要时引入,最简洁。
既然决定引入,就直接选择 Factory。它为弥补现有容器的缺点而生,无论小规模开始还是逐步扩展都足够。先数一数前面提到的四个引入信号,现在符合几个。

