7-20.【高级特性】泛型约束 (T: Equatable, T: AnyObject) 与 where 子句的区别与适用场景?

4 阅读2分钟

在 Swift 中,泛型约束有两种主要的表达方式:**内联约束(Inline Constraints)**和 where 子句(Where Clauses)

虽然它们在很多情况下可以互换,但它们的逻辑侧重点和能力边界有着显著差异。


1. 内联约束 (Inline Constraints)

内联约束直接写在泛型参数列表 <T: Equatable> 中。

  • 特点:简洁、直接。

  • 适用场景

    • 单一协议约束:如 T: Codable
    • 类继承约束:如 T: UIViewController
    • 简单组合:如 <T: Equatable & NSCopying>
  • 局限性:无法处理复杂的类型逻辑,比如关联类型(Associated Types)之间的关系。


2. where 子句 (Where Clauses)

where 子句将约束独立出来,放在声明的末尾。

  • 特点:表现力极强,支持集合关系类型等价性的判断。

  • 适用场景

    1. 约束关联类型:这是内联约束做不到的。

      Swift

      func process<C: Collection>(_ container: C) where C.Element: Equatable {
          // 这里要求集合的元素必须是 Equatable
      }
      
    2. 要求两个泛型类型相同:使用 ==

      Swift

      func compare<S1, S2>(_ s1: S1, _ s2: S2) where S1: Sequence, S2: Sequence, S1.Element == S2.Element {
          // 要求两个序列的元素类型完全一致
      }
      
    3. 多重复杂约束:当约束条件太多,写在 <...> 里会导致代码难以阅读时。


3. 核心区别对比

特性内联约束 (<T: P>)where 子句
可读性适合简单约束,一眼看清适合复杂逻辑,结构清晰
关联类型约束不支持(无法访问 T.Element支持(核心优势)
同值约束 (==)不支持支持
扩展 (Extension)仅限简单的协议实现支持对特定泛型条件的扩展

4. 特殊场景:针对特定条件的 extension

这是 where 子句最强大的舞台。你可以为同一个泛型类型,根据不同的约束提供不同的实现。

Swift

extension Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        return items.last == item
    }
}

extension Stack where Element: Numeric {
    func sum() -> Element {
        return items.reduce(0, +)
    }
}

5. 什么时候用 AnyObject

你在问题中提到了 T: AnyObject。这是一个特殊的约束:

  • 本质:它要求 T 必须是一个类(Class) ,不能是结构体或枚举。

  • 适用场景

    1. 内存管理:当你需要在泛型类中使用 weak 引用时。
    2. 身份比较:当你需要使用 === 指针比较算子时。
    3. 与 ObjC 交互:确保泛型可以无缝桥接到 id 类型。

总结建议

  1. 默认优先使用内联约束,保持代码紧凑。
  2. 涉及关联类型(如 Collection.Element)或类型相等判断==)时,必须使用 where
  3. 约束过多时,为了代码的美观和可维护性,将逻辑移至 where 子句。