Swift中的依赖注入(Dependency Injection):初学者完整指南

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

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

开始课程

引言:为什么你的代码需要依赖注入?



每个iOS开发者都会遇到这样的情况:一个类变得“臃肿”,并且与具体实现紧密耦合。你修改一个类,却不得不重写几十个其他类。熟悉吗?这就是经典的紧耦合问题。



依赖注入(Dependency Injection, DI)是一种解决此问题的设计模式。对象不再自己创建依赖,而是从外部“注入”依赖。这使得代码更灵活、可测试,并符合SOLID原则(特别是依赖反转原则)。



在本文中,我们将探讨Swift中的DI是什么、有哪些实现方式,以及它如何简化iOS/macOS开发者的工作。我们将使用简单易懂的示例,即使是初学者也能理解。



什么是依赖注入?一个简单的例子



想象一下你在做披萨。没有DI的情况:你自己种小麦、磨面粉、挤牛奶做奶酪。这是紧耦合。有DI的情况:你只需向供应商订购原料(依赖)。你不知道面粉是怎么做的——你只关心结果。



在编程中也是如此。假设我们有一个通过电子邮件发送订单的OrderService类:



// ❌ 没有DI——紧耦合class OrderService {    let mailer = Mailer() // 自己创建依赖

func process(order: Order) { mailer.send(email: "新订单:\\(order.id)") }}


问题:我们无法将Mailer替换为测试版本。OrderService类“知道”得太多了。



// ✅ 使用DI——依赖被注入class OrderService {    let mailer: MailerProtocol // 依赖抽象,而非具体实现

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

func process(order: Order) { mailer.send(email: "新订单:\\(order.id)") }}


现在,我们可以传入任何符合MailerProtocol协议的对象——无论是真实的邮件发送器还是用于测试的“替身”。



Swift中依赖注入的三种主要方式



有三种流行的方法可以将依赖传递给类。我们来逐一介绍。



1. 构造函数注入(通过初始化器)

这是最常见且首选的方式。依赖在对象创建时传入。这保证了对象始终处于有效状态。



protocol NetworkServiceProtocol {    func fetchData() -> String}

class RealNetworkService: NetworkServiceProtocol { func fetchData() -> String { return "来自服务器的真实数据" }}

class MockNetworkService: NetworkServiceProtocol { func fetchData() -> String { return "测试数据" }}

class DataManager { private let network: NetworkServiceProtocol

// 依赖通过init传入 init(network: NetworkServiceProtocol) { self.network = network }

func load() -> String { return network.fetchData() }}

// 使用let realManager = DataManager(network: RealNetworkService())let mockManager = DataManager(network: MockNetworkService())


优点: 明确显示需要哪些依赖。对象立即可用。缺点: 如果依赖很多,初始化器会变得臃肿。



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

依赖在对象创建后设置。常用于依赖是可选的,或者在初始化时难以获取的情况。



class UserViewModel {    var apiService: NetworkServiceProtocol? // 可选依赖

func loadUser() { apiServi

博客

书籍推荐