Swift集合类型协议浅析(上)

导 读git

狐友技术团队github

Swift是一门面向协议的语言,协议能够被扩展,来给遵循该协议的类型提供方法等具体实现,经过扩展协议,咱们能够为协议所要求的方法提供默认实现。在Swift出现之前,协议在iOS中就十分重要,想一想UITableViewDataSource 和 UITableViewDelegate 等协议的讨论,能够说他们天天出如今咱们的脑海里;使用Swift编程中必定会用到标准库中的协议,例如Array就是一个继承了10个协议的Struct,Bool类型是一个继承了7个协议的Struct;算法

本篇文章为Swift集合类型协议浅析系列文章的上篇,在这篇(上)中,咱们尝试去解读一些基础协议的内部关系和逻辑,向你展现Swift如此强大的秘密。编程

Sequenceapi

Sequence是一组值的列表,可以提供对元素的顺序、迭代访问。markdown

1protocol Sequence {2   associatedtype Iterator3   func makeIterator() -> Iterator4}5protocol IteratorProtocol {6   associatedtype Element7   mutating func next() -> Element?8}
复制代码

遵循Sequence协议须要有一个名为makeIterator的获取遍历器的方法,返回遍历器Iterator,Iterator遵循IteratorProtocol协议,协议有一个mutating的next方法,这个方法返回Sequence中下一个对象,直到没有返回nil。闭包

举个例子:dom

1struct InfiniteIterator: IteratorProtocol {2  let value: Int3  mutating func next() -> Int? {4    return value5  }6}
复制代码

InfiniteIterator遵循IteratorProtocol协议,因此须要有next方法,这里咱们简化一下,让next的返回值始终是value:ide

1var iterator = InfiniteIterator(value: 24)2iterator.next()   //243iterator.next()   //24
复制代码

你会发现输出始终是24,下面咱们继续实现Sequence协议:函数

1struct InfiniteSequence: Sequence {2  let value: Int3  func makeIterator() -> InfiniteIterator {4    return InfiniteIterator(value: value)  //注意此处5  }6}
复制代码

实现makeIterator方法,返回的类型是上一步刚刚实现的遵循IteratorProtocol的协议对象InfiniteIterator,这样一个遵循Sequence协议的对象就构成了;咱们能够尝试使用Sequence协议的prefix方法,取前几个对象测试:

1let infinite = InfiniteSequence(value: 20)2for value in infinite.prefix(5) {3  print(value)   //204}
复制代码

遵循Sequence的类型均可以使用for in遍历,如:

1let array = [1,2,3]2for item in array {3    print(item)4}
复制代码

那么为何可以这么使用呢?内部的实现相似下面,因为有了迭代器和得到下一个元素的next方法,咱们就能够知道下一个,下一个的下一个,不断重复。

1var iterator = someSequence.makeIterator()2while let element = iterator.next() {3   doSomething(with:element)4}
复制代码

简单小结一下:

(1)Sequence能够是有限序列,也能够是无限序列,如同上面InfiniteSequence就是一个无限序列;

(2)Sequence只能够迭代一次,有些时候能够屡次进行迭代,可是不能保证每次均可以对其屡次迭代。

AnySequence

为了简化建立Sequence须要遵循协议的复杂性,咱们发现标准库帮咱们提供了一个sequence方法:

1func sequence<T>(first: T, next: @escaping (T) -> T?) -> UnfoldFirstSequence<T>
复制代码

这个函数有两个参数,第一个参数须要Sequence序列返回的第一个值,第二个参数是一个闭包,接受以前的sequence元素并返回下一个:

1func infiniteBasic(value: Int) -> UnfoldSequence<Int, (Int?, Bool)> {2 return sequence(first: value) { _ in return value }3}
复制代码

返回类型UnfoldSequence 遵循IteratorProtocol和Sequence,这样会简化上一步,就不须要写两个类分别遵循这两个协议:

1for value in infiniteBasic(value: 24).prefix(5) {2  print(value)3}
复制代码

输出结果仍然是24,与前面的分别实现的效果一致;

而后咱们就来看看这小节的AnySequence,它是一个类型擦除器,官方这样定义:

An instance of AnySequence forwards its operations to an underlying base sequence having the same Element type, hiding the specifics of the underlying sequence.

它本质上并无什么做用,只是用来隐藏内部真实的类型,能够类比OC类型中的id,有类似的做用。AnySequence遵循Sequence协议,因此上面的infiniteBasic能够改造为:

1func infinite(value: Int) -> AnySequence<Int> {2  return AnySequence {3    sequence(first: value) { _ in return value }4  }5}6for value in infinite(value: 24).prefix(5) {7  print(value)    //24  8}
复制代码

AnyIterator

类型擦除序列。AnyIterator是AnySequence的实例,将其操做转发给具备相同元素类型的底层基序列,从而隐藏底层序列的细节。实质是传入一个生成下一个元素的闭包,内部经过next方法日后遍历下一个Element元素类型。

1func infinite(value: Int) -> AnySequence<Int> {2  return AnySequence<Int> {3    AnyIterator<Int> { value }4  }5}
复制代码

AnyIterator中闭包return下一个元素,其中很适合使用defer作索引的+1获-1操做,这样隐藏了IteratorProtocol的实现,好比下面的例子:

1var x=0 2func infinite2(value: Int) -> AnySequence<Int> { 3    return AnySequence<Int> { 4        AnyIterator<Int> { 5            defer { 6                x+=1 7            } 8            return x<15 ? x : nil 9        }10    }11}



1for value in infinite2(value: 24) {2    print(value)3}
复制代码

这段代码会每次不断迭代+1返回,直到15为止。

小结一下目前用到的这几个类型的关系,Sequence协议有Iterator属性,这个属性遵循IteratorProtocol协议,UnfoldSequence协议同时遵循Sequence协议和IteratorProtocol协议,AnySequence遵循Sequence协议,AnySequence也有相同Iterator属性,这个属性遵循AnyIterator协议,而AnyIterator协议又同时遵循IteratorProtocol和Sequence,构成了一个关联关系,因此咱们能够经过AnySequence和AnyIterator的组合构成Sequence。

Collection

collection 是一个有索引的sequence,能够从任何index反复寻址不少次(单向)。

实现一个collection:

  • 定义Comaprable index type;

  • 定义 startIndex;

  • 定义 endIndex,是最后一个元素的下一个;

  • 定义方法 index(after:) 增长index;

  • 定义O(1) subscript operator get only 通关给定index,返回元素element。

咱们来举一个具体的例子,称为Fizz Buzz Collection;咱们要建立一个范围从1到100的集合,打印范围1-100,被3整除时,print Fizz;被5整除时,print Buzz;若是同时可以被3和5整除,print FizzBuzz。

1struct FizzBuzz: Collection { 2 3    typealias Index = Int 4 5    var startIndex: Index { 6        return 1 7    } 8 9    var endIndex: Index {10        return 10111    }1213    func index(after i: Index) -> Index {14        return i + 115    }1617    func index(_ i: Int, offsetBy distance: Int) -> Int {18        return i + distance19    }2021    subscript (index: Index) -> String {22        precondition(indices.contains(index), "out of 1-100")23        switch (index.isMultiple(of: 3), index.isMultiple(of: 5)) {24        case (false, false):25            return String(index)26        case (true, false):27            return "Fizz"28        case (false, true):29            return "Buzz"30        case (true, true):31            return "FizzBuzz"32        }33    }34}
复制代码

Collection继承Sequence,与Sequence不一样的是,Collection再也不多是无限的,你总能知道集合当中有多少个元素,所以咱们能够对集合进行屡次迭代;而对序列而言,通常只能迭代一次;另外,协议中新增的主要元素就是名为Index的新关联类型,Collection的范围经过属性从startIndex到endIndex标记,须要注意的是,endIndex指向最后一个元素的下一个位置,因此是101;另外,Index这个关联类型必须是Comparable,要得到下一个元素会增长index,直到抵达endIndex,这时候迭代会终止。

1for value in FizzBuzz() {2    print(value)3}
复制代码

BidirectionalCollection

双向集合与集合很是相似,只是它多了一个功能。与 Collection 继承自 Sequence 相同,BidirectionalCollection 继承自 Collection,可是双向集合能够向两个方向任意移动。在集合当中,前进咱们已经有了 indexAfter 这个函数,因此为了增长后退的功能,须要再增长一个名为 indexBefore 的新函数,它将可让咱们以相反的顺序来遍历集合。

1protocol Collection {2  //...3  func index(after index: Index) -> Index4}56protocol BidirectionalCollection: Collection {7  func index(before index: Index) -> Index8}9
复制代码

你能够试想一下若是是一个Collection普通集合,若是想要获得最后一个元素该如何处理?

显然你须要一个一个的遍历,直到最后一个元素,这样显然太慢了,咱们更但愿一步跳到最后,并当即将末尾的值返回,如今有了BidirectionalCollection,就很不同了,先检查集合是否为空,若是为空,那么直接返回nil便可;若是不是,咱们就须要取endIndex,而后经过indexBefore获得endIndex的前一个索引,这样就获得了last元素。

1var last: Iterator.Element? {2    guard !self.isEmpty else { return nil }3    let indexOfLastItem = self.index(before: self.endIndex)4    return self[indexOfLastItem]5}
复制代码

RandomAccessCollection

遵循此协议能够更快地访问值。你能够直接跳转到想要获取的那个元素,而没必要去逐步遍历;RandomAccessCollection继承BidirectionalCollection,能够在常量时间访问任何元素的集合,咱们经常使用的Array就是一个例子。

遵循RandomAccessCollection须要实现index(_:offsetBy:)和distance(from:to:)方法或者Index遵循Strideable协议。

MutableCollection

支持集合经过下标的方式改变自身的元素,即 array[index] = newValue。该协议在 Collection 的基础上新增的 API 是下标subscript[5]必须提供的一个setter方法。

RangeReplaceableCollection

支持插入和删除任意区间的元素集合;遵循RangeReplaceableCollection协议须要实现:

(1)空的初始化集合;

(2)实现replaceSubrange(_:with:),RangeReplaceableCollection协议提供了这个方法的默认实现,它用来替换当前集合中指定范围中的元素。目标范围和用来替换集合的长度能够不一样。

咱们再回头看看文章开始处这张集合类型关系的图谱,你会发现RangeReplaceableCollection和MutableCollection位于同一个层级,并无继承关系,一些类型只符合MutableCollection(例如UnsafeMutableBufferPointer[6]),一些只适用于RangeReplaceableCollection(例如String.CharacterView),只有一部分同时遵照,如图中所示的Array:

结语

Sequence 和 Collection 组成了 Swift 中集合类型的根基。而专门性的集合类型 BidirectionalCollection、RandomAccessCollection、MutableCollection 和 RandomAccessCollection 对你自定义的类型和算法的功能和性能特性提供了很是细粒度的控制,构成了强大的功能组合。

参考:/ Github /

相关文章
相关标签/搜索