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


    Swift中,大量内置类如Dictionary,Array,Range,String都使用了协议

    先看看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 等函数式方法可以使用.

  • 相关阅读:
    error和exception有什么区别?
    金额转换,阿拉伯数字的金额转换成中国传统的形式如:(¥1011)->(一千零一拾一元整)输出?
    HTTP请求的GET与POST方式的区别
    解释一下什么是servlet?
    参数Parameters、变量Variables
    数据库事务的四大隔离级别以及处理的问题
    redis安装
    CVB生命周期(APIView源码解析)
    前端页面渲染机制
    Django基础之request
  • 原文地址:https://www.cnblogs.com/weiboyuan/p/6122209.html
Copyright © 2020-2023  润新知