Swift: Swift 协议:接口定义、协议继承与委托模式
协议定义了方法、属性和其他要求的蓝图,是 Swift 面向协议编程(POP)的基石。本课学习如何定义和遵守协议,以及委托模式的实际应用。
1. 你将学到
- 使用
protocol定义属性和方法要求 - 类和结构体遵守协议
- 协议继承与组合
- 通过
extension提供默认实现 - 委托(Delegate)设计模式
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) 协议语法
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) 协议继承
一个协议可以继承一个或多个其他协议:
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) 委托模式
委托模式是一种设计模式——一个对象将部分工作委托给另一个遵守协议的对象:
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 协议不具备的特性。📖 小节
- 协议定义属性和方法的要求,不提供实现
- 类、结构体和枚举都可以遵守协议
- 协议支持继承——一个协议可以继承多个父协议
- 协议组合
&要求类型同时遵守多个协议 extension为协议提供默认实现,遵守类型可以选择覆盖- 委托模式通过弱引用协议属性避免循环引用
📝 作业
- 基础题: 定义一个
Flyable协议,包含fly()方法。创建Bird结构体和Airplane类遵守该协议,各自实现fly()方法。 - 进阶题: 定义
Encryptable协议(func encrypt(_: String) -> String)和Decryptable协议(func decrypt(_: String) -> String),让CaesarCipher类同时遵守两个协议(凯撒密码,偏移量 3)。使用协议组合Encryptable & Decryptable作为参数类型。 - 挑战题: 实现一个「可排序数据源」委托模式:定义
SortableDataSource协议(func numberOfItems() -> Int和func item(at index: Int) -> String),定义SortDelegate协议(func didSort(items: [String]))。创建NameList类遵守SortableDataSource,创建TableView类遵守SortDelegate。NameList排序后通知TableView更新。