编程Swift 中级/高级

什么是 Swift 中的协议组合,它在实践中如何工作,以及何时应该应用它来替代多重继承或常规使用协议?

用 Hintsage AI 助手通过面试

答案。

协议组合是 Swift 中的一个机制,允许创建一个需要同时遵循多个协议的类型。这是替代多重继承的一种方式,而在 Swift 中类是没有多重继承的。

问题背景

Objective-C 仅支持协议的多重继承,而不支持类的多重继承。Swift 继承了这个传统,更加重视协议及其组合来构建新的抽象。

问题

程序员通常面临的任务是创建一个类型,其行为由多个抽象定义。多重继承不可避免地导致层次冲突,而在 Swift 中通过协议和协议组合可以安全地解决这个问题。

解决方案

在 Swift 中,可以使用操作符 '&' 组合协议。这允许创建需要同时遵循多个协议的变量或函数参数。

代码示例:

protocol Drivable { func drive() } protocol Flyable { func fly() } struct FlyingCar: Drivable, Flyable { func drive() { print("驾驶中") } func fly() { print("飞行中") } } func testVehicle(_ vehicle: Drivable & Flyable) { vehicle.drive() vehicle.fly() } testVehicle(FlyingCar())

关键特点:

  • 操作符 '&' 允许将多个协议组合在一起用于变量或函数参数
  • 适用于类、结构体和枚举
  • 允许明确描述所需行为而无需创建中间类型

陷阱问题。

可以将仅实现其中一个协议的对象传递给协议组合的参数吗?

不可以,对象必须实现组合中参与的 所有 协议,否则编译器会报错。

代码示例:

// struct Car: Drivable {} — 不可以传递给 testVehicle,因为没有实现 fly()

协议组合是否仅适用于值而不适用于类型?

协议组合应用于值(变量、函数参数),但不用于定义对象类型(例如,不能声明新类型作为某种 "协议组合" — 只能是变量)。

代码示例:

var obj: SomeProtocol & AnotherProtocol // 合法 // typealias MyType = SomeClass & AnotherProtocol // 错误

可以通过 & 组合类和协议吗?

可以,但有一个限制:左侧只能有一个类类型(类或其子类),其余只能是协议,否则编译器会报错。

代码示例:

class A {} protocol B {} // func f(obj: A & B) {} // 合法 // func f(obj: A & AnotherClass & B) {} // 错误!只允许一个类类型

常见错误和反模式

  • 期望与类的多重继承相同的工作方式
  • 在不必要的情况下使用组合,当一个协议足够时
  • 在使用外部库和协议组合时违规合同

现实生活中的例子

负面案例

在一个项目中,虽然只需要一个协议,但却使用带有协议组合的对象进行层间数据传输:

func present(item: Displayable & Serializable) { ... }

优点:

  • 灵活且可扩展的接口 缺点:
  • 如果大多数传输对象不需要所有功能,可能会导致复杂性和混淆

正面案例

仅在明确的情况下使用协议组合——例如,处理同时支持 Codable 和 Identifiable 以进行通用序列化的对象:

func save<T: Codable & Identifiable>(_ item: T) { ... }

优点:

  • 明确描述对类型的要求
  • 最小化错误 缺点:
  • 可能会稍微复杂化函数的签名