Swift: Swift 协议:接口定义、协议继承与委托模式

协议定义了方法、属性和其他要求的蓝图,是 Swift 面向协议编程(POP)的基石。本课学习如何定义和遵守协议,以及委托模式的实际应用。

1. 你将学到


2. 一个支付开发者的真实故事

(1) 痛点:不同支付方式各自实现,接口不统一

Alice 的团队需要整合多种支付方式——信用卡、PayPal、微信支付:

SWIFT
class CreditCardPayment {
    func processCardPayment(amount: Double, cardNumber: String) -> Bool {
        // 信用卡处理
        return true
    }
}
class PayPalPayment {
    func payWithPayPal(amount: Double, email: String) -> Bool {
        // PayPal 处理
        return true
    }
}
class WeChatPayment {
    func wechatPay(amount: Double, code: String) -> Bool {
        // 微信支付
        return true
    }
}

三个类、三个不同的方法名、三个不同的参数列表。 业务逻辑中到处都是 if-else 判断支付类型,新增一种支付方式就要改所有调用代码。

(2) 协议的解法

SWIFT
protocol PaymentMethod {
    func pay(amount: Double) -> Bool
}
struct CreditCard: PaymentMethod {
    let cardNumber: String
    func pay(amount: Double) -> Bool { /* 处理 */ true }
}
struct PayPal: PaymentMethod {
    let email: String
    func pay(amount: Double) -> Bool { /* 处理 */ true }
}
// 统一处理
func checkout(amount: Double, using method: PaymentMethod) {
    method.pay(amount: amount)
}

不关心里面是什么支付方式,只关心它符合 PaymentMethod 协议。

(3) 收益:统一接口,可扩展

维度 各自实现 协议统一
方法名 各不相同 统一 pay(amount:)
新增支付 改所有调用代码 仅新增遵守类型
测试 每个写一套测试 协议 mock 统一测试
耦合度 低(面向协议编程)

3. 协议的定义与遵守

(1) 协议语法

100%
graph TB
    A[protocol 关键字] --> B[协议名]
    B --> C[属性要求]
    B --> D[方法要求]
    B --> E[下标要求]
    C --> F["var name: String { get set }"]
    D --> G["func work()"]
    E --> H["subscript(...) -> Type"]
协议要求 语法 说明
可读写属性 { get set } 变量 var 或 计算属性(get+set)
只读属性 { get } 常量 let、变量 var 或只读计算属性
实例方法 func name() 只有签名,无实现
mutating 方法 mutating func name() 值类型可修改自身

▶ 示例:协议的定义与遵守

SWIFT
// ============================================
// 定义和遵守协议
// ============================================
// 1. 定义协议
protocol Describable {
    var description: String { get }
    func summarize() -> String
}
// 2. 结构体遵守协议
struct Book: Describable {
    let title: String
    let author: String
    var description: String {
        return "\"\(title)\" by \(author)"
    }
    func summarize() -> String {
        return "Book: \(description)"
    }
}
// 3. 类遵守协议
class Movie: Describable {
    let title: String
    let director: String
    init(title: String, director: String) {
        self.title = title
        self.director = director
    }
    var description: String {
        return "\(title) (directed by \(director))"
    }
    func summarize() -> String {
        return "Movie: \(description)"
    }
}
let book = Book(title: "1984", author: "George Orwell")
let movie = Movie(title: "Inception", director: "Christopher Nolan")
print(book.summarize())
print(movie.summarize())

输出:

TEXT 📖 仅展示
Book: "1984" by George Orwell
Movie: Inception (directed by Christopher Nolan)

4. 协议继承与组合

(1) 协议继承

一个协议可以继承一个或多个其他协议:

100%
graph TB
    A[Protocol: Payable] --> B["属性: amount"]
    A --> C["方法: process()"]
    B --> D[Protocol: Refundable]
    C --> D
    D --> E["方法: refund()"]
    D --> F[Type: CreditCard]
    F --> G["实现 Payable + Refundable"]

▶ 示例:协议继承

SWIFT
// ============================================
// 协议继承——可以退款的支付协议
// ============================================
protocol Payable {
    var amount: Double { get }
    func process() -> Bool
}
protocol Refundable: Payable {  // 继承 Payable
    func refund() -> Bool
}
// CreditCard 实现了可退款支付
struct CreditCard: Refundable {
    let amount: Double
    let cardNumber: String
    func process() -> Bool {
        print("Processing $\(amount) on card \(cardNumber)")
        return true
    }
    func refund() -> Bool {
        print("Refunding $\(amount) to card \(cardNumber)")
        return true
    }
}
// GiftCard 只能支付,不能退款
struct GiftCard: Payable {
    let amount: Double
    let code: String
    func process() -> Bool {
        print("Processing $\(amount) with gift card \(code)")
        return true
    }
}
let payments: [Payable] = [
    CreditCard(amount: 100, cardNumber: "1234"),
    GiftCard(amount: 50, code: "GIFT-001")
]
for payment in payments {
    payment.process()
    // 检查是否也是 Refundable
    if let refundable = payment as? Refundable {
        print("  This payment can be refunded")
    }
}

输出:

TEXT 📖 仅展示
Processing $100.0 on card 1234
  This payment can be refunded
Processing $50.0 with gift card GIFT-001

(2) 协议组合

& 符号组合多个协议,要求类型同时遵守所有协议:

SWIFT
protocol Identifiable {
    var id: String { get }
}
protocol Loggable {
    func log()
}
// 类型必须同时遵守 Identifiable 和 Loggable
func saveItem(_ item: Identifiable & Loggable) {
    print("Saving item \(item.id)")
    item.log()
}
struct User: Identifiable, Loggable {
    let id: String
    func log() { print("User logged: \(id)") }
}
let user = User(id: "U-001")
saveItem(user)

输出:

TEXT 📖 仅展示
Saving item U-001
User logged: U-001

5. 扩展默认实现与委托模式

(1) extension 提供默认实现

通过 extension 为协议方法提供默认实现,遵守类型可以选择不实现:

SWIFT
// ============================================
// 扩展为协议添加默认实现
// ============================================
protocol Greetable {
    var name: String { get }
    func greet() -> String
}
// 默认实现
extension Greetable {
    func greet() -> String {
        return "Hello, \(name)!"
    }
}
// Person 使用默认实现
struct Person: Greetable {
    let name: String
    // 无需实现 greet()——使用默认版本
}
// Robot 自定义实现
struct Robot: Greetable {
    let name: String
    func greet() -> String {
        return "Beep boop, I am \(name)"
    }
}
print(Person(name: "Alice").greet())
print(Robot(name: "R2-D2").greet())

输出:

TEXT 📖 仅展示
Hello, Alice!
Beep boop, I am R2-D2

(2) 委托模式

委托模式是一种设计模式——一个对象将部分工作委托给另一个遵守协议的对象:

100%
sequenceDiagram
    participant A as Class A
    participant D as Delegate (protocol)
    participant B as Class B
    A->>D: 发生了事件
    D->>B: 调用 delegate 方法
    B-->>A: 返回处理结果

▶ 示例:委托模式实现

SWIFT
// ============================================
// 委托模式:下载管理器
// ============================================
// 1. 定义委托协议
protocol DownloadDelegate: AnyObject {
    func downloadDidStart(_ url: String)
    func downloadDidProgress(_ url: String, percent: Double)
    func downloadDidComplete(_ url: String, data: String)
    func downloadDidFail(_ url: String, error: String)
}
// 2. 下载管理器——委托给外部处理事件
class DownloadManager {
    weak var delegate: DownloadDelegate?
    func download(from url: String) {
        delegate?.downloadDidStart(url)
        // 模拟下载过程
        for i in 1...5 {
            let percent = Double(i) / 5.0 * 100
            delegate?.downloadDidProgress(url, percent: percent)
        }
        // 模拟完成
        delegate?.downloadDidComplete(url, data: "Downloaded content from \(url)")
    }
}
// 3. 视图控制器——作为委托
class ViewController: DownloadDelegate {
    func downloadDidStart(_ url: String) {
        print("[UI] 开始下载:\(url)")
    }
    func downloadDidProgress(_ url: String, percent: Double) {
        print("[UI] \(url): \(Int(percent))%")
    }
    func downloadDidComplete(_ url: String, data: String) {
        print("[UI] 下载完成:\(data.prefix(20))...")
    }
    func downloadDidFail(_ url: String, error: String) {
        print("[UI] 下载失败:\(error)")
    }
}
let manager = DownloadManager()
let ui = ViewController()
manager.delegate = ui  // 设置委托
manager.download(from: "https://example.com/file.zip")

输出:

TEXT 📖 仅展示
[UI] 开始下载:https://example.com/file.zip
[UI] https://example.com/file.zip: 20%
[UI] https://example.com/file.zip: 40%
[UI] https://example.com/file.zip: 60%
[UI] https://example.com/file.zip: 80%
[UI] https://example.com/file.zip: 100%
[UI] 下载完成:Downloaded content...

💡 提示: AnyObject 约束要求委托必须是 class 类型(而非 struct),这样可以使用 weak var 避免循环引用。这是委托模式的标准做法。


6. 完整示例:可配置的数据验证器

SWIFT
// ============================================
// 完整示例:数据验证系统
// 功能:协议 + 协议继承 + 扩展默认实现 + 委托模式
// ============================================
import Foundation
// 1. 验证协议体系
protocol Validatable {
    var value: Any { get }
    func validate() -> Bool
}
// 可报告详细错误的验证
protocol DetailedValidatable: Validatable {
    func errorMessage() -> String
}
// 默认实现
extension Validatable {
    func validate() -> Bool { return true }
}
// 2. 具体验证器
struct EmailValidator: DetailedValidatable {
    let value: Any
    func validate() -> Bool {
        guard let email = value as? String else { return false }
        return email.contains("@") && email.contains(".")
    }
    func errorMessage() -> String {
        return "Invalid email format"
    }
}
struct AgeValidator: DetailedValidatable {
    let value: Any
    func validate() -> Bool {
        guard let age = value as? Int else { return false }
        return age >= 18 && age <= 120
    }
    func errorMessage() -> String {
        return "Age must be between 18 and 120"
    }
}
struct NonEmptyValidator: Validatable {
    let value: Any
    // 使用默认的 validate() 返回 true,但我们自定义
    func validate() -> Bool {
        guard let text = value as? String else { return false }
        return !text.isEmpty
    }
}
// 3. 委托——验证结果处理器
protocol ValidationDelegate: AnyObject {
    func validationDidSucceed(for field: String)
    func validationDidFail(for field: String, error: String)
}
// 4. 验证管理器
class ValidationManager {
    weak var delegate: ValidationDelegate?
    private var validators: [(field: String, validator: Validatable)] = []
    func addValidator(for field: String, validator: Validatable) {
        validators.append((field, validator))
    }
    func runAll() -> Bool {
        var allValid = true
        for (field, validator) in validators {
            if validator.validate() {
                delegate?.validationDidSucceed(for: field)
            } else {
                allValid = false
                let error = (validator as? DetailedValidatable)?.errorMessage() ?? "Validation failed"
                delegate?.validationDidFail(for: field, error: error)
            }
        }
        return allValid
    }
}
// 5. 使用
class FormController: ValidationDelegate {
    func validationDidSucceed(for field: String) {
        print("[✓] \(field) is valid")
    }
    func validationDidFail(for field: String, error: String) {
        print("[✗] \(field): \(error)")
    }
}
let manager = ValidationManager()
manager.delegate = FormController()
manager.addValidator(for: "email", validator: EmailValidator(value: "alice@example.com"))
manager.addValidator(for: "age", validator: AgeValidator(value: 25))
manager.addValidator(for: "name", validator: NonEmptyValidator(value: "Alice"))
let allValid = manager.runAll()
print("Form valid: \(allValid)")

输出:

TEXT 📖 仅展示
[✓] email is valid
[✓] age is valid
[✓] name is valid
Form valid: true

❓ 常见问题

Q 协议和基类有什么区别?
A 协议只定义接口蓝图,不存储属性(只能声明 { get set })。基类可以提供存储属性和方法实现。一个类可以遵守多个协议,但只能继承一个基类。面向协议编程(POP)是 Swift 的核心设计哲学。
Q 什么时候用协议默认实现,什么时候用基类?
A 当需要提供共享的存储属性或需要继承的初始化器时用基类。当只需要定义行为接口并提供默认实现时用协议扩展。优先用协议,因为更灵活(多协议遵守)。
Q 为什么委托用 weak var?
A 避免循环引用。DownloadManager 持有 delegate,如果 delegate 也持有 DownloadManager,就会产生强引用循环。weak 让委托引用不增加引用计数。要求委托是 class 是因为 struct 不支持 weak 引用。
Q 协议中的 mutating 方法有什么用?
A 标记方法可以修改遵守类型的自身属性。struct 和 enum 的值类型方法默认不能修改属性,加了 mutating 后才允许。class 实现时不需要写 mutating——类方法默认可以修改属性。
Q @objc 协议有什么用?
A @objc 协议允许协议被 Objective-C 代码使用,常用于 UIKit 委托协议。@objc 协议可以有 optional 要求(可选实现的方法),这是纯 Swift 协议不具备的特性。

📖 小节


📝 作业

  1. 基础题: 定义一个 Flyable 协议,包含 fly() 方法。创建 Bird 结构体和 Airplane 类遵守该协议,各自实现 fly() 方法。
  2. 进阶题: 定义 Encryptable 协议(func encrypt(_: String) -> String)和 Decryptable 协议(func decrypt(_: String) -> String),让 CaesarCipher 类同时遵守两个协议(凯撒密码,偏移量 3)。使用协议组合 Encryptable & Decryptable 作为参数类型。
  3. 挑战题: 实现一个「可排序数据源」委托模式:定义 SortableDataSource 协议(func numberOfItems() -> Intfunc item(at index: Int) -> String),定义 SortDelegate 协议(func didSort(items: [String]))。创建 NameList 类遵守 SortableDataSource,创建 TableView 类遵守 SortDelegateNameList 排序后通知 TableView 更新。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏