朋友们,下午好。 特别是针对
“ iOS开发人员”课程的学生
。 “高级课程”,我们准备了文章“ Swift中泛型的力量”的第二部分的翻译。

相关类型,where子句,下标等在文章
“ Swift中泛型的力量。 第1部分介绍了通用功能,通用类型和类型限制。 如果您是初学者,建议您先阅读第一部分,以更好地理解。
定义协议时,有时将一个或多个相关类型声明为该定义的一部分很有用。 关联的类型为用作协议一部分的类型指定存根名称。 在使用协议之前,不会指定用于此相关类型的实际类型。 关联类型是使用
associatedtype
关键字声明的。
我们可以为在
第一部分中创建的堆栈定义协议。
protocol Stackable { associatedtype Element mutating func push(element: Element) mutating func pop() -> Element? func peek() throws -> Element func isEmpty() -> Bool func count() -> Int subscript(i: Int) -> Element { get } }
Stackable
协议定义了任何堆栈应提供的必要功能。
符合
Stackable
协议的任何类型都必须能够指定其存储的值的类型。 应该确保仅将正确类型的元素添加到堆栈中,并且应该清楚其下标返回哪种类型的元素。
让我们根据协议更改堆栈:
enum StackError: Error { case Empty(message: String) } protocol Stackable { associatedtype Element mutating func push(element: Element) mutating func pop() -> Element? func peek() throws -> Element func isEmpty() -> Bool func count() -> Int subscript(i: Int) -> Element { get } } public struct Stack<T>: Stackable { public typealias Element = T var array: [T] = [] init(capacity: Int) { array.reserveCapacity(capacity) } public mutating func push(element: T) { array.append(element) } public mutating func pop() -> T? { return array.popLast() } public func peek() throws -> T { guard !isEmpty(), let lastElement = array.last else { throw StackError.Empty(message: "Array is empty") } return lastElement } func isEmpty() -> Bool { return array.isEmpty } func count() -> Int { return array.count } } extension Stack: Collection { public func makeIterator() -> AnyIterator<T> { var curr = self return AnyIterator { curr.pop() } } public subscript(i: Int) -> T { return array[i] } public var startIndex: Int { return array.startIndex } public var endIndex: Int { return array.endIndex } public func index(after i: Int) -> Int { return array.index(after: i) } } extension Stack: CustomStringConvertible { public var description: String { let header = "***Stack Operations start*** " let footer = " ***Stack Operation end***" let elements = array.map{ "\($0)" }.joined(separator: "\n") return header + elements + footer } } var stack = Stack<Int>(capacity: 10) stack.push(element: 1) stack.pop() stack.push(element: 3) stack.push(element: 4) print(stack)
扩展现有类型以指示相关类型
您可以扩展现有类型以符合协议。
protocol Container { associatedtype Item mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get } } extension Array: Container {}
将约束添加到关联的类型:
您可以在协议中为关联类型添加限制,以确保关联类型符合这些限制。
让我们更改
Stackable
协议。
protocol Stackable { associatedtype Element: Equatable mutating func push(element: Element) mutating func pop() -> Element? func peek() throws -> Element func isEmpty() -> Bool func count() -> Int subscript(i: Int) -> Element { get } }
现在,堆栈元素的类型应匹配
Equatable
,否则将发生编译时错误。
递归协议限制:
该协议可能是其自身要求的一部分。
protocol SuffixableContainer: Container { associatedtype Suffix: SuffixableContainer where Suffix.Item == Item func suffix(_ size: Int) -> Suffix }
Suffix
有两个限制:它必须符合
SuffixableContainer
协议(在此定义协议),并且其
Item
类型必须与容器的
Item
类型匹配。
Swift标准库中的
Protocol Sequence
有一个很好的例子
Protocol Sequence
说明这一主题。
关于递归协议限制的建议:
https :
//github.com/apple/swift-evolution/blob/master/proposals/0157-recursive-protocol-constraints.md通用类型扩展:
扩展通用类型时,在定义扩展时不会描述类型参数的列表。 相反,扩展主体中提供了来自源定义的类型参数的列表,并且源类型的参数名称用于引用源定义的类型参数。
extension Stack { var topItem: Element? { return items.isEmpty ? nil : items[items.count - 1] } }
通用where子句
对于相关类型,定义需求很有用。
通用的where子句描述了需求。 通用的
where
子句允许您要求关联的类型符合特定协议,或者某些类型参数和相关类型相同。 通用
where
子句以
where
关键字开头,后跟相关类型的约束或类型与相关类型之间的相等关系。 Generic
where
子句直接写在类型或函数体的大括号之前。
func allItemsMatch<C1: Container, C2: Container> (_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable { }
具有一般条件的扩展,其中
您可以将通用
where
子句用作扩展的一部分。 下面的示例通过添加
isTop (_ :)
方法扩展了先前示例的总体
Stack
结构。
extension Stack where Element: Equatable { func isTop(_ item: Element) -> Bool { guard let topItem = items.last else { return false } return topItem == item } }
该扩展仅在堆栈上的项目支持Equatable时才添加
isTop (_ :)
方法。 您还可以将泛型
where
子句与协议扩展一起使用。 您可以
where
使用逗号将它们添加到
where
。
将类型与Generic子句相关联,其中:
您可以在关联类型中包含通用
where
子句。
protocol Container { associatedtype Item mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get } associatedtype Iterator: IteratorProtocol where Iterator.Element == Item func makeIterator() -> Iterator }
对于从另一个协议继承的协议,可以向继承的绑定类型添加约束,包括协议声明中的常规
where
子句。 例如,以下代码声明了
ComparableContainer
协议,该协议要求
Item
支持
Comparable
:
protocol ComparableContainer: Container where Item: Comparable { }
通用类型别名:
类型别名可以具有公共参数。 它仍将保留为别名(即不会引入新类型)。
typealias StringDictionary<Value> = Dictionary<String, Value> var d1 = StringDictionary<Int>() var d2: Dictionary<String, Int> = d1
在这种机制中,不能使用对类型参数的附加限制。
这样的代码将不起作用:
typealias ComparableArray<T where T : Comparable> = Array<T>
通用上标
下标可以使用通用机制,并包括通用
where
子句。 您将类型名称写在下
subscript
之后的尖括号中,并将通用
where
子句写在下标主体的大括号之前。
extension Container { subscript<Indices: Sequence>(indices: Indices) -> [Item] where Indices.Iterator.Element == Int { var result = [Item]() for index in indices { result.append(self[index]) } return result } }
通用专业化
通用专业化意味着编译器会为特定参数类型(例如Int)克隆通用类型或函数(例如Stack
<
T
>
。 然后可以专门针对Int优化此专用功能,同时将删除所有多余的功能。 在编译时用类型实参替换类型参数的过程称为
专门化 。
通过专门针对这些类型的泛型函数,我们可以消除虚拟调度,内联调用(在必要时)的成本,并消除泛型系统的开销。
操作员超载
默认情况下,泛型类型不适用于运算符,为此,您需要一个协议。
func ==<T: Equatable>(lhs: Matrix<T>, rhs: Matrix<T>) -> Bool { return lhs.array == rhs.array }
关于泛型的有趣的事情
为什么不能为泛型定义
静态存储属性?
这将需要为每个单独的通用(T)专业化单独存储属性。
深入研究的资源:
https://github.com/apple/swift/blob/master/docs/Generics.rsthttps://github.com/apple/swift/blob/master/docs/GenericsManifesto.mdhttps://developer.apple.com/videos/play/wwdc2018/406/https://www.youtube.com/watch?v=ctS8FzqcRughttps://medium.com/@vhart/protocols-generics-and-existential-containers-wait-what-e2e698262ab1
仅此而已。 在
课程上见。