类型擦除

22 阅读2分钟

下面我会为你提供一个简单的对比示例,展示如何使用 any类型擦除来处理相同的场景,以便理解它们之间的区别。

示例场景

我们定义一个简单的协议 Component,它有一个 render 方法用于渲染内容。然后,我们分别用 any类型擦除来处理这个协议。

1. 使用 any Component

import UIKit

// 定义一个协议
protocol Component {
    func render() -> String
}

// Button 组件实现 Component 协议
struct Button: Component {
    func render() -> String {
        return "Rendering a Button"
    }
}

// Label 组件实现 Component 协议
struct Label: Component {
    func render() -> String {
        return "Rendering a Label"
    }
}

// 使用 `any Component` 处理多种类型的 Component
func renderComponent(_ component: any Component) {
    print(component.render())
}

// 使用示例
let button: any Component = Button()
let label: any Component = Label()

renderComponent(button)  // 输出: Rendering a Button
renderComponent(label)   // 输出: Rendering a Label

2. 使用类型擦除

import UIKit

// 定义一个协议
protocol Component {
    func render() -> String
}

// Button 组件实现 Component 协议
struct Button: Component {
    func render() -> String {
        return "Rendering a Button"
    }
}

// Label 组件实现 Component 协议
struct Label: Component {
    func render() -> String {
        return "Rendering a Label"
    }
}

// 类型擦除包装类
class AnyComponentBox {
    func render() -> String {
        fatalError("Must override")
    }
}

class ComponentBox<Base: Component>: AnyComponentBox {
    private let base: Base
    
    init(_ base: Base) {
        self.base = base
    }
    
    override func render() -> String {
        return base.render()
    }
}

// 类型擦除包装结构体
struct AnyComponent {
    private let box: AnyComponentBox
    
    init<Base: Component>(_ base: Base) {
        box = ComponentBox(base)
    }
    
    func render() -> String {
        return box.render()
    }
}

// 使用示例
let anyButton = AnyComponent(Button())
let anyLabel = AnyComponent(Label())

print(anyButton.render())  // 输出: Rendering a Button
print(anyLabel.render())   // 输出: Rendering a Label

3. 区别分析

  • any 方式
    • 优点:简单直接。可以轻松处理不同的 Component 类型。
    • 缺点:当协议涉及泛型、关联类型或者 Self 时,any 无法处理这些情况。
  • 类型擦除方式
    • 优点:能够保留类型信息,即便协议涉及泛型、关联类型或 Self。通过 ComponentBox 封装不同的组件,你可以在需要时访问实际的类型和方法。
    • 缺点:代码复杂度较高,因为需要定义额外的类型擦除包装类和结构体。

总结

  • any 适合简单的场景,易于理解和使用。
  • 类型擦除 适合需要保留复杂类型信息、进行泛型约束或者处理协议中关联类型的场景。