Swift 语言中提供了一种 for .. in 语法的形式,用于遍历集合,比如对于 Array 类型,就可以用 for .. in 来进行遍历。这个语法在很多其他语言中也有提供,省去了我们定义下标的操作。今天我们要了解的就是关于 for .. in 语法的原理,我们可以让我们自己的类也支持这个语法。

何为 for .. in

首先,我们先来了解一下 for .. in 的用法,比如这段代码:

let bookList = ["Swift", "iOS", "Objc"]

for bookName in bookList {

    print(bookName)

}

我们定义了一个数组 bookList, 里面存放了三个字符串。然后我们就可以通过 for ... in 循环进行遍历。

数组其实就是 Array 类,我们上面的定义如果写的详细些,应该是这样:

let bookList:Array = ["Swift", "iOS", "Objc"]

也就是说,我们传递给 for ... in 语法的,其实是一个 Array 类的实例。那么我们再来看看 Array 类的继承关系:

public struct Array<Element> : CollectionType, MutableCollectionType, _DestructorSafeContainer {
...
}

它继承自一个叫做 CollectionType 的协议,然后我们再来看一下 CollectionType 的定义:

public protocol SequenceType {
...
}

经过这么一连串的追溯,其实关键就在于这个 SequenceType,一个类如果实现了 SequenceType 协议,那么他就可以使用 for ... in 语法进行遍历了。包括我们自己的定义的类。

如何实现 SequenceType 协议

那么,既然我们知道了这个特性,我们就可以让自己定义的类也支持 for .. in 语法。我们先定义一个实体类 Book:

class Book {

    var name:String = ""
var price:Float = 0.0 init(name: String, price: Float) { self.name = name
self.price = price } }

Book 类有两个属性,一个是书名,一个是价格,然后还有一个构造方法。

接下来,我们再定义一个类 BookList,它实现了 SequenceType 协议,用来表示 Book 实例的列表。不过再实现之前,我们先看一看 SequenceType 协议都需要实现那些接口:

class BookList: SequenceType {

    ...

    typealias Generator = BookListGenerator

    func generate() -> Generator {

        return BookListGenerator(bookList: self.bookList!)

    }

}

SequenceType 协议中定义了一个 typealias Generator 的属性,这个属性是一个继承自 GeneratorType 的类。

SequenceType 还定义了一个 generate 方法,用于返回我们指定的 GeneratorType 类型。

恩。。 怎么又多了个 GeneratorType, 好像有点复杂的样子。那么咱们继续看,GeneratorType 是实际生成遍历信息的接口,我们这里的 BookListGenerator 实现了这个协议,那就来看一下代码吧:

class BookListGenerator : GeneratorType {

    typealias Element = Book

    var currentIndex:Int = 0
var bookList:[Book]? init(bookList: [Book]) { self.bookList = bookList } func next() -> Element? { guard let list = bookList else { return nil } if currentIndex < list.count { let element = list[currentIndex]
currentIndex++
return element }else { return nil } } }

代码稍长,请听我给大家一一分解~

  1. 首先,GeneratorType 定义了一个属性别名: typealias Element。 我们将 Book 类赋值给它,表示我们这个集合中存储的数据类型是 Book 类的实例。

  2. 接下来,GeneratorType 还定义了一个 next 方法。用于遍历这个集合,直到 next 方法返回 nil 的时候,遍历结束。

func next() -> Element? {

    guard let list = bookList else { return  nil }

    if currentIndex < list.count {

        let element = list[currentIndex]
currentIndex++
return element }else { return nil } }
  1. next 方法中,先用 guard 关键字进行了一次判断,检查 bookList(也就是实际的数据是否为空),如果为空,就直接返回 nil。 宣告遍历结束~
  2. 接下来,用了一个叫做 currentIndex 的属性表示当前所遍历到得索引,这个属性的初始值是 0,然后每遍历一个元素,就加 1,直到它的值超出 list.count 的值,就会返回 nil,宣告遍历完成~

这样,我们的 BookListGenerator 就定义完成了(当然,它还声明了一个构造方法,由于实在简单,我们就不多说了~)。再次回到继承自 SequenceType 的 BookList 类中:

class BookList: SequenceType {

    private var bookList:[Book]?

    init() {

        self.bookList = [Book]()

    }

    func addBook(book:Book){

        self.bookList?.append(book)

    }

    typealias Generator = BookListGenerator

    func generate() -> Generator {

        return BookListGenerator(bookList: self.bookList!)

    }

}

这次列出了所有的代码,还是一一分解~

看了上面关于 BookListGenerator 类的定义,相信就不难理解这里的代码了:

typealias Generator = BookListGenerator

func generate() -> Generator {

    return BookListGenerator(bookList: self.bookList!)

}

这两个 SequenceType 接口的方法我们再来观摩下,typealias 就不用多说了,generate 方法会再遍历开始的时候调用一次,每次遍历都会构建一个 Generator 实例,我们这个 BookList 中构建的就是 BookListGenerator,并传入了 self.bookList(这个是实际的数据列表)以供 BookListGenerator 来进行具体的遍历操作。

其他方面嘛,BookList 类还定了一个私有属性,用于实际存放 Book 的列表数据:

private var bookList:[Book]?

还提供了一个构造方法,和一个 addBook 方法,供我们使用,这两个方法比较简单,就不多说啦。

使用我们的 SequenceType 类型

好了,我们的 BookList 就这样完工啦。现在轮到我们检验一下了:

let bookList = BookList()

bookList.addBook(Book(name: "Swift", price: 12.5))
bookList.addBook(Book(name: "iOS" , price: 10.5))
bookList.addBook(Book(name: "Objc", price: 20.0)) for book in bookList { print("\(book.name) 价格 ¥\(book.price)") }

大功告成,我们声明了 BookList 类,然后用 addBook 方法添加几本书进来。接着我们就可以用 for .. in 来遍历这个集合啦。

结语

经过这一系列的折腾,我们实现了 SequenceType 和 GeneratorType 类型的定义,并实现 for .. in 的循环遍历。以及了解了这背后的原理。当然,我在这里也只是给大家介绍了一个点,大家还可以在 swiftdoc.org 查看这几个协议的详细文档,里面介绍的更加全面。

另外,关于 Swift 语言特性知识的内容,还可以看一看这几篇内容:

最后,感谢大家花了这么长时间把这篇文章看完。希望给大家提供更多有价值的内容,期待大家的宝贵意见。

SequenceType 与 GeneratorType的更多相关文章

  1. Swift55个协议的分类和讲解分析

    首先我只想问:为什么是协议?为什么面向协议编程?如果我们回到过去那段年少无知少不更事的面相对象编程时期,我们很多人最初学习的是Objective-C,这意味着我们免受多继承的专横.又或者你是这个房间里 ...

  2. Swift编程语言SequenceType协议中的一些比较有用的接口

    在Swift编程语言中,大部分容器类(比如Array.Dictionary)都实现了SequenceType协议.SequenceType协议中有不少有趣且简便的方法可用来实现我们不少实际需求.这里将 ...

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

    Swift中,大量内置类如Dictionary,Array,Range,String都使用了协议 先看看Hashable 哈希表是一种基础的数据结构.,Swift中字典具有以下特点:字典由两种范型类型 ...

  4. typealias和泛型接口

    typealias 是用来为已经存在的类型重新定义名字的,通过命名,可以使代码变得更加清晰.使用的语法也很简单,使用 typealias 关键字像使用普通的赋值语句一样,可以将某个已经存在的类型赋值为 ...

  5. Swift开发第九篇——Any和AnyObject&typealias和泛型接口

    本篇分为两部分: 一.Swift中的Any和AnyObject 二.Swift中的typealias和泛型接口 一.Swift中的Any和AnyObject 在 Swift 中,AnyObject 可 ...

  6. swifter技巧(100)

    一.swift新元素 Tip1:柯里化 将方法进行柯里化,把接受多个参数的方法变换成接受第一个参数的方法,并且返回接受余下的参数,返回结果的新方法. func addTwoNumbers(a: Int ...

  7. Swift语法总结补充(一)

    Swift基础语法学习总结Swift高级语法学习总结Swift语法总结补充(一) 1. 可选类型是一种类型,String?就是Optional<String>,所以函数参数也可以声明为它2 ...

  8. RxSwift 之官方文档

    RxSwift 官方文档结构 Introduction: Subjects Transforming Observables Filtering Observables Combining Obser ...

  9. 大神都在看的RxSwift 的完全入坑手册

    大神都在看的RxSwift 的完全入坑手册 2015-09-24 18:25 CallMeWhy callmewhy 字号:T | T 我主要是通过项目里的 Rx.playground 进行学习和了解 ...

随机推荐

  1. 多事务运行并发问题spring学习笔记——数据库事务并发与锁详解

    多事务运行并发问题 在实际应用中,往往是一台(或多台)服务器向无数客户程序提供服务,当服务器查询数据库获取数据时,如果没有采用必要的隔离机制,可能会存在数据库事务的并发问题,下面是一些常见的并发问题分 ...

  2. thinkphp,onethink,thinkox验证码不显示

    使用验证码的时候,一开始正常,后来不显示了 网上说是utf-8的编码问题,什么bom去掉,转化为无bom的格式 我都试了,没用 后来知道是在调用验证码的地方  写上 Public function v ...

  3. BootStrap让两个控件在一行显示

    <div class="row"> <div> <label class="form-inline">参加单位:<in ...

  4. spring 输出mvc

    http://flysnowxf.iteye.com/blog/1187580 http://viralpatel.net/blogs/spring-requestheader-example/ 基于 ...

  5. 使用DOT语言和Graphviz绘图(翻译)

    Casa Taloyum About Me Blog Archives 使用DOT语言和Graphviz绘图(翻译) Date Wed 26 November 2014 Tags graphviz / ...

  6. [Grid Layout] Use auto-fill and auto-fit if the number of repeated grid tracks is not to be def

    What about the situation in which we aren’t specifying the number of columns or rows to be repeated? ...

  7. @RequiresPermissions 解释

    @RequiresAuthentication 验证用户是否登录,等同于方法subject.isAuthenticated() 结果为true时. @RequiresUser 验证用户是否被记忆,us ...

  8. springmvc-Controller类的方法返回String不跳转

    买了本书,打算系统的学习一下spring,做了一下书中的练习,出现了一个问题,Controller类的方法返回String,但是页面不跳转,而是直接把字符串的内容显示到页面上. @RequestMap ...

  9. android获取和展示音乐的频谱

    做了个音乐播放器 就一直想做个加一个音乐频谱的展示界面 觉的这是一个好玩的东西,可以将耳边动听的声音形象化,仿佛眼前可以看到声音一样. 但是我在文档的开发者指南里没有讲任何有关音乐频谱的东西,最后还是 ...

  10. iOS app审核参考信息地址

    发件人:(苹果开发支持邮箱地址) 中国区电话:400-670-1855 chinadev<chinadev@asia.apple.com>   您好: 感谢您与 Apple 开发者计划支持 ...