具有associatedtype和where限制的协议是Swift中一种强大的类型抽象机制。这通常用于实现泛型协议,其中具体类型由实现决定。历史上,在早期版本的Swift中,具有associatedtype的协议不能作为具体类型(existential types)使用,这限制了这些协议在集合和接口中的应用。后来,Swift添加了使用where条件来限制associatedtype的机制,这使得可以为复杂场景创建灵活且安全的抽象。
问题:经常会出现创建一个协议的任务,它允许抽象出具体的集合或数据处理器。然而,标准协议可能不够灵活:并非所有类型都可以在不知道相关类型的情况下进行泛化,而具有associatedtype的协议的继承逻辑并不总是显而易见。
解决方案:使用where限制来明确实现要求,从而允许创建具有适应性行为的协议。
代码示例:
protocol Storage { associatedtype Element func add(_ item: Element) } // 限制协议只存储数字 protocol NumericStorage: Storage where Element: Numeric { func sum() -> Element } struct IntStorage: NumericStorage { private var items: [Int] = [] func add(_ item: Int) { items.append(item) } func sum() -> Int { items.reduce(0, +) } }
关键特点:
可以将具有associatedtype的协议用作类型(例如,用于集合)吗?
不,可以直接使用。具有associatedtype的协议是PAT(带有关联类型的协议),不能用作existential type。例如,不能声明数组[Storage],只能通过类型消除实现。
如何为具有associatedtype的协议实现类型消除?
通过一个辅助包装器,该包装器隐藏实际的类型实现。
struct AnyStorage<T>: Storage { private let _add: (T) -> Void init<S: Storage>(_ storage: S) where S.Element == T { _add = storage.add } func add(_ item: T) { _add(item) } }
associatedtype与协议的泛型参数有什么区别?
associatedtype定义了必须根据实现的具体类型,而泛型参数在协议或函数中显式指定,但在协议声明中不允许使用。协议在语法上不能是泛型,只能通过associatedtype。
开发者尝试使用[Storage]来存储任何集合。代码无法编译,只能进行隐式转换或使用Any/不安全的方式。
优点:
缺点:
开发者为隐藏具体实现设计了AnyStorage<T>,并添加了where限制以确保仅与所需类型正常工作。
优点:
缺点: