0Pricing
Swift Academy · 课时

使用 any 的存在类型与类型擦除

使用 any Protocol,了解其性能代价,以及何时应优先使用泛型。

使用 any 的存在类型与类型擦除 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 4 节课。

什么是存在类型?

存在类型会将任何遵循协议的值放入一个盒子中,通过统一的接口访问,并在运行时解析。

protocol Drawable { func draw() }
struct Line: Drawable { func draw() { print("Line") } }
let d: any Drawable = Line()
d.draw()

any 关键字

Swift 5.7 要求在用作存在类型的协议名称前添加 any 关键字,以明确表示装箱。

func render(_ shape: any Drawable) {
  shape.draw()
}
render(Line())

存在类型的开销

存在类型会使用堆分配和动态派发,因此可能比采用静态派发的泛型更慢。

// Existential – dynamic dispatch, potential heap alloc
var shapes: [any Drawable] = [Line(), Line()]
// Generic – static dispatch, no boxing
func drawGeneric<T: Drawable>(_ s: T) { s.draw() }

带关联类型的协议与存在类型问题

带有关联类型的协议不能在不进行类型擦除的情况下作为裸存在类型使用。

protocol Container { associatedtype Item }
// var c: any Container // Swift 5.7 allows with "any" but loses Item info

类型擦除模式

类型擦除包装器会将具体类型封装在结构体之后,从而隐藏具体类型。

struct AnyDrawable: Drawable {
  private let _draw: () -> Void
  init<D: Drawable>(_ d: D) { _draw = d.draw }
  func draw() { _draw() }
}

AnySequence 与 AnyPublisher

Swift 标准库和 Combine 提供了 AnySequence、AnyPublisher 等类型擦除包装器。

import Combine
let subject = PassthroughSubject<Int, Never>()
let pub: AnyPublisher<Int, Never> = subject.eraseToAnyPublisher()

eraseToAnyPublisher()

eraseToAnyPublisher()会隐藏具体的发布者链,使调用方只依赖AnyPublisher。

func fetch() -> AnyPublisher<String, Error> {
  URLSession.shared.dataTaskPublisher(for: URL(string: "https://example.com")!)
    .map { String(data: $0.data, encoding: .utf8) ?? "" }
    .eraseToAnyPublisher()
}

适合使用存在类型的场景

当您需要处理异构集合,或在编译时不知道确切类型时,可以使用存在类型。

var widgets: [any Drawable] = [] // mixed concrete types OK

优先使用泛型而不是存在类型

在可行的情况下,请使用泛型替代存在类型,以获得静态派发和更好的性能。

// Prefer this:
func draw<T: Drawable>(_ item: T) { item.draw() }
// Over this:
func draw(_ item: any Drawable) { item.draw() }

打开存在类型

Swift 5.7 及更高版本在调用泛型函数时可以“打开”存在类型,自动提取其底层类型。

func drawGeneric<T: Drawable>(_ d: T) { d.draw() }
let d: any Drawable = Line()
drawGeneric(d) // compiler opens the existential automatically

类型擦除与不透明类型

类型擦除会隐藏类型,以便进行异构使用;不透明类型则会为单一且一致的返回类型保留类型身份。

let erased: any Drawable = Line()   // runtime type unknown
let opaque: some Drawable = Line()   // compile-time fixed type

快速检查

存在类型的主要性能开销是什么?

课程回顾

存在类型(any Protocol)支持运行时多态,但代价是动态派发。类型擦除会将具体类型封装在稳定的接口之后。为了性能,请优先使用泛型;对于异构集合,请使用存在类型。

常见问题解答

「使用 any 的存在类型与类型擦除」课时是免费的吗?

是的 — 「使用 any 的存在类型与类型擦除」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 4 节课。

「使用 any 的存在类型与类型擦除」这节课中我会学到什么?

使用 any Protocol,了解其性能代价,以及何时应优先使用泛型。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Swift Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「使用 any 的存在类型与类型擦除」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Swift Academy 课中编写并运行代码吗?

能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 泛型约束与 where 子句
  2. 使用 some 关键字的不透明类型
  3. 使用 any 的存在类型与类型擦除
  4. 主要关联类型与类型化抛出
← 返回 Swift Academy