Swift中的依赖注入:原理、示例与实现

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

什么是依赖注入以及为什么在 Swift 中需要它

依赖注入(Dependency Injection,DI)是一种设计模式,对象从外部获取其依赖项,而不是自行创建它们。在 iOS 和 macOS 的 Swift 上下文中,DI 有助于使代码更灵活、可测试和可维护。您不是通过 init() 或 let 硬性绑定类,而是通过构造函数、属性或方法传递依赖项。

DI 的主要优点:

  • 可测试性:您可以轻松地在单元测试中将真实对象替换为模拟对象或桩对象。
  • 低耦合:类依赖于抽象(协议),而不是具体实现。
  • 简化重构:代码某一部分的更改不会破坏另一部分。
  • 清晰的架构:代码遵循单一职责原则和依赖反转原则(SOLID 中的 DIP)。

在 Swift 中,DI 在使用 UIKit、SwiftUI、CoreData、网络和其他服务时尤其重要。没有它,您可能会面临“上帝对象”和代码重用困难的风险。



Swift 中依赖注入的主要方式

1. 通过初始化器注入(Constructor Injection)

这是最常见且推荐的方式。依赖项在对象创建时传递,并变为不可变(let)。

protocol NetworkServiceProtocol {    func fetchData() -> String}

class NetworkService: NetworkServiceProtocol { func fetchData() -> String { return "Данные с сервера" }}

class ViewModel { private let networkService: NetworkServiceProtocol

// 通过初始化器注入 init(networkService: NetworkServiceProtocol) { self.networkService = networkService }

func loadData() -> String { return networkService.fetchData() }}

// 使用let service = NetworkService()let viewModel = ViewModel(networkService: service)print(viewModel.loadData()) // "Данные с сервера"

优点:依赖项明确,对象始终处于有效状态。缺点:当依赖项数量较多时,构造函数会变得冗长。



2. 通过属性注入(Property Injection)

依赖项在对象创建后设置。通常用于可选服务或使用 Storyboard/XIB 时。

class UserViewController: UIViewController {    var analyticsService: AnalyticsServiceProtocol?

override func viewDidLoad() { super.viewDidLoad() analyticsService?.trackEvent("Экран открыт") }}

// 注入let vc = UserViewController()vc.analyticsService = AnalyticsService()

缺点:属性必须是可选的或具有默认值,如果忘记设置依赖项,可能导致运行时错误。



3. 通过方法注入(Method Injection)

依赖项作为方法参数传递。适用于依赖项仅在某次操作中需要的情况。

class DataManager {    func processData(using service: NetworkServiceProtocol) -> String {        let rawData = service.fetchData()        return "Обработано: \\(rawData)"    }}

let manager = DataManager()let result = manager.processData(using: NetworkService())print(result) // "Обработано: Данные с сервера"


在 iOS 应用中实现 DI 的示例

考虑一个实际示例:一个用于显示用户列表的应用。我们将创建带有依赖注入的协议和类。

// 1. 协议protocol UserRepositoryProtocol {    func getUsers() -> [String]}

protocol UserViewModelProtocol { var users: [String] { get } func loadUsers()}

// 2. 仓库实现class UserRepository: UserRepositoryProtocol {

博客

书籍推荐