用于 Swift (iOS/macOS) 的依赖注入容器:Swinject
在现代 iOS 和 macOS 的 Swift 开发中,架构模式对于创建可维护和可测试的代码起着关键作用。实现依赖倒置原则(Dependency Inversion Principle)最强大的工具之一是 Swinject——一个用 Swift 编写的轻量级但功能丰富的 DI 容器。Swinject 允许集中管理对象的创建及其依赖关系,使开发者免于手动构建对象图。
什么是依赖注入以及为什么需要容器?
依赖注入(DI)是一种技术,对象从外部获取其依赖项,而不是自行创建。这降低了代码耦合度并简化了单元测试。然而,在大型项目中,手动依赖注入会导致代码臃肿。这时 DI 容器就派上了用场,它可以:
- 自动化对象的创建和关联
- 管理对象的生命周期(单例、新实例)
- 简化实现的替换(例如,用于测试)
- 提高代码的可读性和可维护性
安装 Swinject
Swinject 可以通过 Swift Package Manager、CocoaPods 或 Carthage 安装。对于 SPM,请在 Package.swift 中添加:
dependencies: [ .package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0")]或者通过 CocoaPods:
pod 'Swinject', '~> 2.8'Swinject 的核心概念
该库基于几个关键组件:
- Container — 主要的依赖注册器和解析器
- Service — 被注册的类型(通常是协议或类)
- Component — 服务的具体实现
- Scope — 定义对象的生命周期(transient, graph, container, weak)
实践示例:注册和解析依赖
考虑一个典型场景——一个带有认证服务和使用该服务的控制器的应用程序。
// 服务协议protocol AuthServiceProtocol { func login(username: String, password: String) -> Bool}
// 实现class AuthService: AuthServiceProtocol { func login(username: String, password: String) -> Bool { // 认证逻辑 return username == "admin" && password == "1234" }}
// 依赖于 AuthServiceProtocol 的控制器class LoginViewController { private let authService: AuthServiceProtocol // 通过初始化器注入 init(authService: AuthServiceProtocol) { self.authService = authService } func authenticate(user: String, pass: String) { if authService.login(username: user, password: pass) { print("登录成功") } else { print("数据无效") } }}
// 配置容器let container = Container()container.register(AuthServiceProtocol.self) { _ in AuthService() }container.register(LoginViewController.self) { r in LoginViewController(authService: r.resolve(AuthServiceProtocol.self)!)}
// 使用let controller = container.resolve(LoginViewController.self)!controller.authenticate(user: "admin", pass: "1234") // 登录成功生命周期管理(Scopes)
Swinject 支持四种内置作用域:
- .transient — 每次创建新实例(默认)
- .graph — 每个解析图一个实例(大多数情况下默认使用)
- .container — 容器范围内的单例
- .weak — 弱引用,对