从Swift3的标准库协议看面向协议编程(一)

本文主要是从 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) 
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.. 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 等函数式方法可以使用.

你可能感兴趣的:(从Swift3的标准库协议看面向协议编程(一))