本文主要是從 Hashable作為入口,瞭解Swift3的標準庫協議.再研究從自定義的類去遵循 Sequence和 Collection 兩大基礎協議,從而加深Swift的面向協議程式設計的思想
入坑 Swift的時候看了一段演講,這是連結.這讓我對Swift的面向協議程式設計有了興趣.
Swift中,大量內建類如Dictionary,Array,Range,String都使用了協議,在學習的過程中我把自己的學習筆記記錄下來.
2016-09-14: 更新完 Xcode8以後,根據swift2.2寫好的,又要重寫一遍了.....
##先看看Hashable 雜湊表是一種基礎的資料結構.,Swift中字典具有以下特點:字典由兩種範型型別組成,其中 key 必須實現 Hashable 協議.關於 swift 中字典是怎麼實現的,可以看這篇 .
public protocol Hashable : Equatable {
public var hashValue: Int { get }
}
複製程式碼
可以看到 Hashable遵循了 Equable,那再來看看 Equable
public protocol Equatable {
public func ==(lhs: Self, rhs: Self) -> Bool
}
複製程式碼
看來遵循 Equable 都必須過載這個 == ,來定義自己的判等方法. 上 sample:
struct MyPoint: Hashable, Comparable {
var x: Int
var y: Int
var hashValue: Int {
get {
return x.hashValue + y.hashValue
}
}
}
func ==(lhs: MyPoint, rhs: MyPoint) -> Bool {
return lhs.hashValue == rhs.hashValue
}
let pointA = MyPoint(x: 1, y: 1)
let pointB = MyPoint(x: 1, y: 1)
let pointC = MyPoint(x: 1, y: 2)
pointA == pointB //true
pointA == pointC //false
複製程式碼
如果需要比較大小需要遵循 Comparable這個協議,協議需要內容就不貼了,直接上 code:
func <(lhs: MyPoint, rhs: MyPoint) -> Bool {
return lhs.hashValue < rhs.hashValue
}
func <=(lhs: MyPoint, rhs: MyPoint) -> Bool {
return lhs.hashValue <= rhs.hashValue
}
func >(lhs: MyPoint, rhs: MyPoint) -> Bool {
return lhs.hashValue > rhs.hashValue
}
func >=(lhs: MyPoint, rhs: MyPoint) -> Bool {
return lhs.hashValue >= rhs.hashValue
}
pointA >= pointB //true
pointA > pointC //false
pointA < pointC //true
複製程式碼
借用 Mattt的話來做下總結:
在 Swift 中,Equatable 是一個基本型別,由此也演變出了 Comparable 和 Hashable 兩種型別。這三個一起組成了這門語言關於物件比較的核心元素。
##再看看Sequence (這部分 Swift3有變化) ####SequenceType(Swift 2.x) -> Sequence (Swift 3.0) SequenceType在喵神的 Swifttips 裡面已經講解的很好了,我還是把自己的例子寫了下來.這部分程式碼是 Swift3版本下的.
public protocol SequenceType {
associatedtype Iterator : IteratorProtocol
//在3.0以前是GeneratorType
........
}
複製程式碼
IteratorProtocol又是什麼呢?其實GeneratorType一樣,可以理解為生成器
public protocol IteratorProtocol {
associatedtype Element
public mutating func next() -> Self.Element?
}
複製程式碼
associatedtype Element要求實現這個協議的類必須定義一個名為Element的別名,這樣一定程度上實現了泛型協議。協議同時要求實現next函式,其返回值是別名中定義的Element型別,next函式代表生成器要生成的下一個元素。 sample 是來自連結,寫的非常清楚,我這只是貼貼我的 playground
struct Book {
var name: String = ""
var price: Float = 0.0
init(name: String, price: Float) {
self.name = name
self.price = price
}
}//定義一個 Book 的 Struct, 有書名和價格
class BookListIterator: IteratorProtocol {
typealias Element = Book //將 Book 類賦值 Element
var currentIndex: Int = 0
var bookList: [Book]?
init(bookList: [Book]) {
self.bookList = bookList
} //初始化方法
//用來遍歷 bookList,直到返回 nil
func next() -> BookListIterator.Element? {
guard let list = bookList else { return nil }
if currentIndex < list.count {
let element = list[currentIndex]
currentIndex += 1
return element
} else {
return nil
}
}
}
複製程式碼
現在IteratorProtocol這個生成器已經寫好了,可以寫 Sequence了
class BookList: Sequence {
var bookList: [Book]?
init() {
self.bookList = [Book]()
}
func addBook(book: Book) {
self.bookList?.append(book)
}
// associatedtype Iterator : IteratorProtocol
typealias Iterator = BookListIterator
//public func makeIterator() -> Self.Iterator
func makeIterator() -> BookList.Iterator {
return BookListIterator(bookList: self.bookList!)
}
}
複製程式碼
來試試寫的 BookList:
let bookList = BookList()
bookList.addBook(book: Book(name: "Swift", price: 12.5))
bookList.addBook(book: Book(name: "iOS" , price: 10.5))
bookList.addBook(book: Book(name: "Objc", price: 20.0))
for book in bookList {
print("\(book.name) 價格 ¥\(book.price)")
}
// Swift 價格 ¥12.5
// iOS 價格 ¥10.5
// Objc 價格 ¥20.0
複製程式碼
而且不止可以使用 for...in, 還可以用 map , filter 和 reduce. 再談談 Swift3的變化,其實就是變了GeneratorType To IteratorProtocol,就是這麼任性....
##從 Sequence 到 Collection ####SequenceType(Swift 2.x) -> Sequence (Swift 3.0) 如果現在我們要看 bookList的 count, 就牽扯到了Collection這個協議,可以發現這個協議是對Indexable 和 Sequence 的擴充套件. ####重點看看這個Indexable 在2.x的時候,Indexable 並沒有繼承任何其他協議,那麼3.0來了,來了個IndexableBase:
public protocol Indexable : IndexableBase
複製程式碼
那再來看IndexableBase:
//2.x版本indexable
var endIndex: Self.Index
var startIndex: Self.Index
subscript(_: Self.Index)
//新增的下標以及例項方法
subscript(_: Range<Self.Index>)
func formIndex(after:)
func index(after:)
複製程式碼
再回到 Collection, 如果我們的型別已經遵循了Sequence,那麼就只需要遵循:
var startIndex: Int
var endIndex: Int
subscript(_: Self.Index)
func index(after:)
複製程式碼
這四個需求中,startIndex和endIndex是為了 Collection 中要遵循 Indexable協議,還得實現一個下標索引來獲取對應索引元素.在 Swift3中,還需要宣告 index(after:),關於這個戳swift-evolutionl連結. 再看看怎麼對 Sample 例子中BookList遵循 Collection
extension BookList: Collection {
typealias Element = Book
var startIndex: Int {
return 0
}
var endIndex: Int {
return bookList!.count
}
subscript(i: Int) -> Element {
precondition((0..<endIndex).contains(i), "index out of bounds")
return bookList![i]
}
func index(after i: Int) -> Int {
if i < endIndex {
return i + 1
} else {
return endIndex
}
}
}
複製程式碼
是幾個屬性方法的實現還是挺簡單的,現在BookList 這個 class,既遵循了 Sequence 和 Collection, 就有超過40種方法和屬性可以使用:
booklist.first //(Book(name: "Swift", price: 12.5))
booklist.count // 3
booklist.endIndex // 3
booklist.isEmpty //false
複製程式碼
現在自己建立的型別就已經遵循了 Sequence和 Collection,還有map,reduce 等函式式方法可以使用.