Swift中的依赖注入:iOS/macOS的原理与示例

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

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

开始课程

Swift 中的依赖注入简介



依赖注入(Dependency Injection,简称 DI)是一种关键的设计原则,有助于在 Swift 中创建灵活、可测试且易于维护的应用程序。在 iOS 和 macOS 开发中,DI 将对象的创建与使用分离开来,从而显著简化了代码的测试和修改。



核心思想是:类不应自行创建其依赖项。相反,依赖项应从外部“注入”。这是 SOLID 原则中依赖倒置原则(Dependency Inversion Principle)的实现。



为什么依赖注入对 iOS/macOS 开发很重要?



在原生 Swift 应用程序中,经常遇到紧耦合(tight coupling)的问题。来看一个典型示例:



class UserService {    let networkManager = NetworkManager()    let database = Database()        func fetchUser(id: Int) -> User {        // 逻辑    }}


这里 UserService 与 NetworkManager 和 Database 的具体实现紧密绑定。这会带来几个问题:



  • 测试困难 — 无法用模拟对象(mocks)或桩(stubs)替换真实服务
  • 可复用性低 — 该类无法与其他实现一起使用
  • 违反单一职责原则(SRP) — 该类不仅处理业务逻辑,还负责创建依赖项


依赖注入通过允许通过构造函数、属性或方法注入依赖项来解决这些问题。



Swift 中实现依赖注入的方式



1. 构造函数注入(Constructor Injection)



最常用且推荐的方式。依赖项通过初始化器传递:



protocol NetworkService {    func request(endpoint: String) -> Data}

protocol DatabaseService { func save(user: User)}

class UserService { private let networkService: NetworkService private let databaseService: DatabaseService init(networkService: NetworkService, databaseService: DatabaseService) { self.networkService = networkService self.databaseService = databaseService } func fetchUser(id: Int) -> User? { let data = networkService.request(endpoint: "users/\\(id)") // 解析数据 return User(id: id, name: "John") }}


优点:依赖项明确可见,是类工作的必要条件,易于测试。



2. 属性注入(Property Injection)



依赖项在对象创建后设置:



class AuthManager {    var networkService: NetworkService?        func login(username: String, password: String) -> Bool {        guard let service = networkService else { return false }        // 身份验证逻辑        return true    }}

// 使用let authManager = AuthManager()authManager.networkService = RealNetworkService()


缺点:依赖项可能未设置,导致使用可选类型并可能引发崩溃。



3. 方法注入(Method Injection)



依赖项直接传递给方法:



class DataProcessor {    func process(data: Data, using service: NetworkService) -> ProcessedData {        let result = service.request(endpoint: "process")        // 处理        return ProcessedData()    }}


适用于依赖项仅在某个方法中需要的情况。



实践示例:任务管理应用



我们来创建一个使用 DI 的简单 iOS 应用:



// 依赖项协议protocol TaskRepository {    func fetchTasks() ->                

博客

书籍推荐