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. 中小研发团队架构实践之RabbitMQ快速入门及应用

    原文:中小研发团队架构实践之RabbitMQ快速入门及应用 使用过分布式中间件的人都知道,程序员使用起来并不复杂,常用的客户端API就那么几个,比我们日常编写程序时用到的API要少得多.但是分布式中间 ...

  2. A Guide to Python's Magic Methods

    Book Source:[https://rszalski.github.io/magicmethods/] magic methods: 名称前后有双下划线的方法 构造函数和初始化 初始化类实例时, ...

  3. Lucene学习总结之八:Lucene的查询语法,JavaCC及QueryParser 2014-06-25 14:25 722人阅读 评论(1) 收藏

    一.Lucene的查询语法 Lucene所支持的查询语法可见http://lucene.apache.org/java/3_0_1/queryparsersyntax.html (1) 语法关键字 + ...

  4. 从Ecipse中导出程序至apk 分类: H1_ANDROID 2013-10-26 22:17 516人阅读 评论(0) 收藏

    若未有数字证书: 1. 2. 3. 4. 5. 若已有数字证书: 上面的后3步改为 版权声明:本文为博主原创文章,未经博主允许不得转载.

  5. php实现二叉树遍历

    php实现二叉树遍历 一.总结 关注输入输出 二.php实现二叉树遍历 题目描述 编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储). 例如如下的先序遍历字符串 ...

  6. Make chrome extension

    How to Make a Chrome Extension. https://robots.thoughtbot.com/how-to-make-a-chrome-extension Skip to ...

  7. Android注冊短信验证码功能

    一.短信验证的效果是通过使用聚合数据的SDK实现的 ,效果例如以下: 二.依据前一段时间的博客中输了怎么注冊! 注冊之后找到个人中心找到申请一个应用就可以! 三.依据官方文档创建项目 官方文档API下 ...

  8. 静态编译ltrace

    ltrace可以跟踪进程的库函数调用,它会显现出哪个库函数被调用,而strace则是跟踪程序的每一个系统调用. 有时候只使用strace还是不够的,须要ltrace配合才干找出问题出在哪里. 假设在b ...

  9. boost_1_34_1在c++builder6编译(把所有dll文件复制到windows系统目录,所以lib文件复制到bcb6\lib目录)

    boost_1_34_1.zip boost 正则表达式 bcb6编译boost_1_34 有个项目要对大量的文本信息进行分析,以前的方法是自己写函数然后进行分析.现在发现一个正则表达式的处理方法,其 ...

  10. 数组类型转换失败:NSMutableArray和NSArray的相互转换

    1.数组类型转换无效(错误) @property(strong, nonatomic) NSMutableArray *temp_Array; _temp_Array=(NSMutableArray ...