Swinject — Swift(iOS/macOS)的依赖注入容器
依赖注入(DI)是 SOLID 原则中的关键原则之一,它允许创建松耦合、可测试且可扩展的代码。在 Swift 的 iOS 和 macOS 开发世界中,实现 DI 的最流行库之一是 Swinject。这是一个轻量级但功能强大的依赖注入容器,简化了对象及其生命周期的管理。
在本文中,我们将详细探讨 Swinject 的工作原理、如何安装以及在真实项目中使用它。
什么是依赖注入以及为什么需要容器?
依赖注入是一种技术,对象从外部获取其依赖项,而不是自己创建它们。这提高了灵活性并简化了测试。DI 容器(例如 Swinject)自动化了对象创建和绑定的过程,使开发者免于编写样板代码。
安装 Swinject
Swinject 可以通过 Swift Package Manager、CocoaPods 或 Carthage 安装。我们来看最现代的方式——SPM:
// 在 Xcode 中:File → Swift Packages → Add Package Dependency// URL:https://github.com/Swinject/Swinject.git// 版本:2.8.0 及以上或者将其添加到 Package.swift:
dependencies: [ .package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0")]Swinject 的主要组件
- Container — 注册和解析依赖项的主要对象。
- Service — 我们想要注入的类型(协议或类)。
- Registration — 将抽象(协议)与具体实现绑定的过程。
- Scope — 管理对象的生命周期(瞬态、单例等)。
基本使用示例
假设我们有一个协议 NetworkServiceProtocol 及其实现 NetworkService:
import Swinject
protocol NetworkServiceProtocol { func fetchData() -> String}
class NetworkService: NetworkServiceProtocol { func fetchData() -> String { return "数据已获取" }}
// 创建容器let container = Container()
// 注册依赖项container.register(NetworkServiceProtocol.self) { _ in NetworkService() }
// 解析(获取)实例let service = container.resolve(NetworkServiceProtocol.self)!print(service.fetchData()) // "数据已获取"在 ViewController(iOS)中注入依赖项
最常见的是,Swinject 用于将服务注入到控制器中。我们来看一个带有视图模型的示例:
import Swinject
// 视图模型的协议和实现protocol UserViewModelProtocol { func getUsers() -> [String]}
class UserViewModel: UserViewModelProtocol { private let networkService: NetworkServiceProtocol init(networkService: NetworkServiceProtocol) { self.networkService = networkService } func getUsers() -> [String] { // 使用 networkService 加载数据 return ["Alice", "Bob"] }}
// 在容器中注册container.register(UserViewModelProtocol.self) { resolver in let network = resolver.resolve(NetworkServiceProtocol.self)! return UserViewModel(networkService: network)}
// 在控制器中使用class UsersViewController: UIViewController { var viewModel: UserViewModelProtocol? override func viewDidLoad() { super.viewDidLoad() viewModel = container.resolve(UserViewModelProtocol.self) print(viewModel?.getUsers() ?? []) }}对象的生命周期(Scopes)
Swinject 支持多种作用域:
- .transient
图书馆中也有
推荐