在 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 子句将约束独立出来,放在声明的末尾。
-
特点:表现力极强,支持集合关系和类型等价性的判断。
-
适用场景:
-
约束关联类型:这是内联约束做不到的。
Swift
func process<C: Collection>(_ container: C) where C.Element: Equatable { // 这里要求集合的元素必须是 Equatable } -
要求两个泛型类型相同:使用
==。Swift
func compare<S1, S2>(_ s1: S1, _ s2: S2) where S1: Sequence, S2: Sequence, S1.Element == S2.Element { // 要求两个序列的元素类型完全一致 } -
多重复杂约束:当约束条件太多,写在
<...>里会导致代码难以阅读时。
-
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) ,不能是结构体或枚举。 -
适用场景:
- 内存管理:当你需要在泛型类中使用
weak引用时。 - 身份比较:当你需要使用
===指针比较算子时。 - 与 ObjC 交互:确保泛型可以无缝桥接到
id类型。
- 内存管理:当你需要在泛型类中使用
总结建议
- 默认优先使用内联约束,保持代码紧凑。
- 涉及关联类型(如
Collection.Element)或类型相等判断(==)时,必须使用where。 - 约束过多时,为了代码的美观和可维护性,将逻辑移至
where子句。