Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain

第4章:协议与泛型的结合

4.4 使用 Self 和协议的动态分派

在 Swift 的协议中,Self 是一个特殊的类型别名,表示遵循协议的具体类型本身。与泛型和关联类型结合,Self 允许协议定义涉及遵循者类型本身的方法,从而实现类型安全的自我引用行为。同时,协议的动态分派机制决定了这些方法的调用方式,尤其在多态场景中表现出色。本节将深入探讨 Self 的用法、动态分派的原理及其在协议化编程(POP)中的应用,通过示例展示其功能和设计考量。

什么是 Self?

Self 是协议中的隐式类型,表示遵循该协议的具体类型。它常用于定义需要与遵循者自身交互的方法,例如比较、操作或返回相同类型的实例。例如:

protocol Equatable {
    static func == (lhs: Self, rhs: Self) -> Bool
}
  • Self 表示遵循 Equatable 的具体类型(如 Int、String)。
  • == 方法要求比较两个相同类型的实例。

使用 Self 的场景

Self 通常出现在以下情况:

  1. 方法参数:需要与遵循者自身交互。
  2. 返回值:方法返回遵循者类型本身。
  3. 类型约束:确保操作限定在同一类型。

例如,一个简单的协议:

protocol Clonable {
    func clone() -> Self
}

struct Document: Clonable {
    var content: String
    
    func clone() -> Document { // 返回类型可以是具体类型
        return Document(content: content)
    }
}

let doc = Document(content: "Report")
let cloned = doc.clone()
print(cloned.content) // 输出: Report
  • clone() 返回 Self,即 Document 类型。
  • 遵循者必须返回自己的类型,确保类型安全。

Self 与动态分派

当通过协议类型调用方法时,Swift 使用动态分派来确定具体实现。这种机制依赖见证表(witness table),在运行时查找遵循者的实现。结合 Self,动态分派支持多态行为。例如:

protocol Operable {
    func combine(with other: Self) -> Self
}

struct Number: Operable {
    var value: Int
    
    func combine(with other: Number) -> Number {
        return Number(value: value + other.value)
    }
}

struct Text: Operable {
    var value: String
    
    func combine(with other: Text) -> Text {
        return Text(value: value + other.value)
    }
}

let num = Number(value: 5)
let text = Text(value: "Hello")

let operableNum: Operable = num
let operableText: Operable = text

let resultNum = operableNum.combine(with: num)
print((resultNum as! Number).value) // 输出: 10

let resultText = operableText.combine(with: text)
print((resultText as! Text).value) // 输出: HelloHello
  • operableNum 和 operableText 是 Operable 类型,调用 combine(with:) 时使用动态分派。
  • Self 确保参数和返回值与调用者类型一致。

Self 的限制与解决

Self 的使用有一些限制,尤其在协议类型上下文中:

  • 不能作为独立类型:Self 只能在协议定义中使用,不能直接声明变量为 Self 类型。
  • 协议类型擦除:当协议作为类型使用时,Self 的具体类型被擦除,需要类型转换或泛型解决。

例如,直接使用 Self 会遇到问题:

func operate(_ item: Operable) -> Operable {
    return item.combine(with: item) // 编译错误:无法确定 Self 类型
}

解决方法是使用泛型:

func operate<T: Operable>(_ item: T) -> T {
    return item.combine(with: item)
}

let combinedNum = operate(num)
print(combinedNum.value) // 输出: 10
  • T: Operable 约束 T 为具体类型,保留 Self 的类型信息。

动态分派与性能

动态分派通过见证表查找实现,带来少量运行时开销:

  • 值类型:如果直接通过具体类型(如 Number)调用,通常是静态分派,性能高。
  • 协议类型:通过 Operable 调用时,使用动态分派,可能影响性能。
  • 优化:在性能敏感场景,可尽量使用具体类型或添加 final(对于类)启用静态分派。

例如:

final class FinalNumber: Operable {
    var value: Int
    init(value: Int) { self.value = value }
    
    func combine(with other: FinalNumber) -> FinalNumber {
        return FinalNumber(value: value + other.value)
    }
}

let finalNum = FinalNumber(value: 3)
let directResult = finalNum.combine(with: finalNum) // 静态分派

实际应用示例

让我们设计一个支持链式操作的协议:

protocol Chainable {
    var value: Int { get }
    func chain(_ other: Self) -> Self
}

extension Chainable {
    func chain(_ other: Self) -> Self {
        fatalError("Must override chain method")
    }
}

struct Counter: Chainable {
    var value: Int
    
    func chain(_ other: Counter) -> Counter {
        return Counter(value: value + other.value)
    }
}

let c1 = Counter(value: 1)
let c2 = Counter(value: 2)
let c3 = Counter(value: 3)

let result = c1.chain(c2).chain(c3)
print(result.value) // 输出: 6
  • Chainable 使用 Self 定义链式操作。
  • 通过具体类型调用,静态分派确保高效执行。

注意事项

  1. 类型一致性:Self 要求参数和返回值与调用者类型相同,避免类型混淆。
  2. 多态限制:在协议类型中,Self 可能需要泛型辅助以保持灵活性。
  3. 默认实现谨慎:为带 Self 的方法提供默认实现时,需确保通用性。

小结

Self 通过自我引用增强了协议的类型安全性,而动态分派则支持了协议的多态性。两者结合,让协议能够在抽象与具体实现之间找到平衡,尤其适用于需要类型一致性或链式操作的场景。下一节将通过实战案例设计一个泛型网络请求框架,进一步展示这些概念的实际应用。


Last Updated:: 3/10/25, 2:58 PM