本文主要講解 Range 家族類的一些實現細節和 Swift 中面向協議編程的一些具體表現。爲了方便起見,不管是 class 或者 struct 都統稱爲『類』。git
在 Swift 4.0 以前 Range 家族一共有 4 種類型:github
let rang: Range = 0.0..<1.0 // 半開區間 let closedRange: ClosedRange = 0.0...1.0 // 閉區間 let countableRange: CountableRange = 0..<1 // Countable 半開區間 let countableClosedRange: CountableClosedRange = 0...1 // Countable 閉區間
以後 Swift 4.0 上新增了 4 種類型:編程
let partialRangeThrough: PartialRangeThrough = ...1.0 // 單側區間 let partialRangeFrom: PartialRangeFrom = 0.0... // 單側區間 let partialRangeUpTo: PartialRangeUpTo = ..<1.0 // 單側區間 let countablePartialRangeFrom: CountablePartialRangeFrom = 1... // Countable 單側區間
但到了 Swift 4.2 又只剩下 5 種類型,分別是:Range
、ClosedRange
、PartialRangeThrough
、PartialRangeFrom
、PartialRangeUpTo
,全部的 Countable 類型都是對應的 typealias
。swift
public typealias CountableRange<Bound> = Range<Bound> public typealias CountableClosedRange<Bound> = ClosedRange<Bound> public typealias CountablePartialRangeFrom<Bound> = PartialRangeFrom<Bound>
Range 的全部類型都是一個擁有 Bound 泛型的 struct,而且這個 Bound 必須繼承 Comparable 協議。app
public struct Range<Bound> where Bound : Comparable public struct ClosedRange<Bound> where Bound : Comparable public struct PartialRangeThrough<Bound> where Bound : Comparable public struct PartialRangeFrom<Bound> where Bound : Comparable public struct PartialRangeUpTo<Bound> where Bound : Comparable
在 swift 標準庫中絕大多數基礎類型都實現了此協議,因此包括 String
、Date
和 IndexPath
等。ide
let stringRange = "a"..<"z" let dateRange = Date()...Date() let indexRange = IndexPath(item: 0, section: 0)...IndexPath(row: 1, section: 0)
當須要用一個自定義的類建立 Range 也只是須要繼承 Comparable 協議,並實現相應方法便可,例如idea
struct Foo: Comparable { var value: Int static func < (lhs: Foo, rhs: Foo) -> Bool { return lhs.value < rhs.value } init(_ v: Int) { value = v } } let range = Foo(1)...Foo(20) foo.contains(Foo(2)) // true
並且 contains(:)
也被自動的實現了,這其實歸功於 RangeExpression 協議:spa
public func contains(_ element: Self.Bound) -> Bool
究其緣由是每一個 Range 類型都有一個 extension:當泛型 Bound 遵照 Comparable 時擴展相應的類以現實 RangeExpression 協議。code
extension Range : RangeExpression where Bound : Comparable
extension ClosedRange : RangeExpression where Bound : Comparable
extension PartialRangeThrough : RangeExpression where Bound : Comparable
extension PartialRangeFrom : RangeExpression where Bound : Comparable
extension PartialRangeUpTo : RangeExpression where Bound : Comparable
試想一下若是用面向對象的語言通常是如何實現 contains(:)
方法的?orm
前面講到在 Swift 4.2 上全部的 Countable 類型都是 typealias,是否具備 Countable 能力被抽象到泛型 Bound 上,以 ClosedRange 爲例
extension ClosedRange : Sequence where Bound : Strideable, Bound.Stride : SignedInteger { /// A type representing the sequence's elements. public typealias Element = Bound /// A type that provides the sequence's iteration interface and /// encapsulates its iteration state. public typealias Iterator = IndexingIterator<ClosedRange<Bound>> }
能夠看到爲了繼承 Sequence 協議,泛型 Bound 須要先繼承 Strideable
,Strideable
協議定義以下:
public protocol Strideable : Comparable { /// A type that represents the distance between two values. associatedtype Stride : Comparable, SignedNumeric public func distance(to other: Self) -> Self.Stride public func advanced(by n: Self.Stride) -> Self }
它有一個綁定類型 Stride 和兩個須要實現的方法,那麼Bound.Stride : SignedInteger
表示的就是Strideable
的綁定類型 Stride
須要繼承 SignedInteger
。
總結下來 swift 經過泛型約束、協議綁定類型約束再結合 extension 能力,把 Countable 能力被抽象到泛型 Bound 上,最終由泛型 Bound 來決定 Range 是否具備 Sequence 能力。
或許你只知道經過 Int 建立的 Range,它就是一個CountableRange
,然而爲何是?首先 Int 繼承於 FixedWidthInteger
, SignedInteger
public struct Int : FixedWidthInteger, SignedInteger
SignedInteger 又繼承於 BinaryInteger
, SignedNumeric
public protocol SignedInteger : BinaryInteger, SignedNumeric { }
BinaryInteger 在必定條件下又繼承於 Strideable
public protocol BinaryInteger : CustomStringConvertible, Hashable, Numeric, Strideable where Self.Magnitude : BinaryInteger, Self.Magnitude == Self.Magnitude.Magnitude
繼續查看BinaryInteger
對 Strideable
實現:
extension BinaryInteger { public func distance(to other: Self) -> Int public func advanced(by n: Int) -> Self }
會發現 Stride 類型就是 Int, 而 Int 自己就是繼承於 SignedInteger
,這樣子就符合前面提到的 Bound.Stride : SignedInteger
條件。最後別忘了另一個限定條件
where Self.Magnitude : BinaryInteger, Self.Magnitude == Self.Magnitude.Magnitude
Magnitude 是 Numeric
協議的綁定類型,Numeric
定義以下:
public protocol Numeric : Equatable, ExpressibleByIntegerLiteral { associatedtype Magnitude : Comparable, Numeric public var magnitude: Self.Magnitude { get } }
但未發現 BinaryInteger
有任何的 extension 給定 Magnitude 的類型。這隻能說明 Magnitude 會在具體的類上被指定,回到 Int 上果真找到 Magnitude
。
public struct Int : FixedWidthInteger, SignedInteger { public typealias Magnitude = UInt }
繼續查看 UInt
public struct UInt : FixedWidthInteger, UnsignedInteger { public typealias Magnitude = UInt }
UnsignedInteger
又繼承於 BinaryInteger
public protocol UnsignedInteger : BinaryInteger { }
因此Self.Magnitude : BinaryInteger, Self.Magnitude == Self.Magnitude.Magnitude
就至關於 Int.UInt : BinaryInteger, Int.UInt == Int.UInt.UInt
。至此 Int 類型知足了一切條件,事實上不只是 Int 整個 Int 家族和 UInt 家族類型都是符合這些條件,下面是關於Int
和 UInt
粗略協議繼承關係。
+---------------+ | Comparable | +-------+-------+ ^ | +-------------+ +-----+-------+ +------>+ Numeric | | Strideable | | +------------++ +-----+-------+ | ^ ^ | | | +-------+-------+ +---+----------+----+ | SignedNumeric | | BinaryInteger | +------+--------+ +---+-----+-----+---+ ^ +-----------^ ^ ^----------+ | | | | +------+---------++ +-----------+--------+ +----+-------------+ | SignedInteger | | FixedWidthInteger | | UnsignedInteger | +---------------+-+ +-+----------------+-+ +--+---------------+ ^ ^ ^ ^ | | | | | | | | ++--------+-+ ++-------+--+ |Int family | |UInt family| +-----------+ +-----------+
struct Foo { var value: Int init(_ v: Int) { value = v } } extension Foo: Strideable { func distance(to other: Foo) -> Int { return other.value - self.value } func advanced(by n: Int) -> Foo { var result = self result.value += n return result } }
Foo 繼承 Strideable 的同時其綁定也被指定爲 Int,這樣子就能夠建立自定義類型的 Range 了,而且繼承於 Sequence 。
let fooRange = Foo(1)...Foo(20) fooRange.contains(Foo(2)) Array((Foo(1)..<Foo(20))) for item in fooRange { print(item) }
Swift 做爲一門面向協議編程的語言,在 Range 的實現上可見一斑,隨着 SE-0142、SE-0143提案分別在 Swift 4.0 和 Swift 4.2 中被加入以後更是增強了在這方面的能力。