目录


1. CollectionType protocol

工欲善其事必先利其器。对于面向对象编程来说,面向接口编程优于面向继承编程(Swift和Objective-C一样都是单继承)。接口在Swift就是protocol. Swift从一开始就是一门protocol oriented programming language, 其强大的protocol功能(包括泛型, extension的默认实现,associated type等)给其其他语言无法比拟的灵活性和全面性。Swift内建了70多个公共接口,这些每个接口就像梯子的每个台阶一样,从各个角度来定义某种接口需要的属性和方法,同时在一个又一个的完美的台阶上,搭建出了例如Array, NSDictionary等功能丰富的内置类。理解Swift内置接口对于实现我们自己的类起着事半功倍的效果—我们要解决的问题,swift工程师可能已经有成熟的解决方案了。让我们借着用CollectionType这个protocol实现”for…in”快速枚举和下标索引取值这两个功能来看看swift的内置协议。

我对CollectionType的定义:

ColletionType(集合类): 即可快速遍历又可下标索引的类。解释: 继承自SequenceTypeIndexable

有很多Swift内置类实现了CollectionType,如Array, Dictionary, Set等。下面我们将分别看看SequenceTypeIndexable这两个协议

2. SequenceType protocol

我对SequenceType的定义

SequenceType(序列类):可以用for…in进行快捷遍历。 解释: 其本质是对GeneratorType进行包装, 有一个产生GeneratorType的工厂方法generate() -> Generator, Generator在其关联类别名中定义。

下面是SequenceType 在Swift 文档中的定义,供参考:

1
2
3
4
5
SequenceType protocol:A Type that can be iterated with a for...in loop。

typealias Generator: GeneratorType // a type that provides the sequence's iteration interface and encapsutlates its iteration state.

func generate() ->Self.Generator //required, return a generator over the elements of this sequence.

我对GeneratorType的定义

GeneratorType(生成器):一个生成一个序列的机器,解决下一个生成”哪个”“类”的实例的问题。解释: 生成器犹如一个自动乒乓机, 你告诉他弹出什么(关联类别名Element),以及弹出哪个(next() ->Element?(可以正向或者反向弹出,或者弹出Int序列,作为Array的下标)。每次调用next(),就像按一个开关,自动乒乓机就弹出乒乓球(或者其他类)或者nil。但是GeneratorType实例没有重置方法。换言之,出来的乒乓球不可逆,如果要重新开始,就要再实例化一个GeneratorType。而SequnceTypegenerator()方法就是GeneratorType的工厂方法, 它的for…in语法就是对generator()next()的调用的封装。因此SequenceType可以有multi-pass, 每次用for…in快速遍历,就实例化一个GeneratorType

下面是GeneratorType在Swift文档中的定义供参考

1
2
3
4
5
GeneratorType protocol: Encapsulates iteration state and interface for interation over a sequence.

typealias Element //the type of element generated by self

mutating func next() ->Self.Element //required, advance to the next element and return it, or nil if no next element exists.

下面我们举一个简单的例子来实现我们一个BookList的for…in 功能。

2.1 SequenceType 实现for…in遍历

问题: 我们有一个自定义的类BookList,里面放着Book类,实现BookList的for…in枚举方法,可以正向遍历或者反向遍历。

2.1.1 GeneratorType protocol 正向遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//  这是Book类,作为BookList的元素
class Book {
    var name = ""
    var price = 0.0
    init(name:String, price: Double){
        self.name = name
        self.price = price
    }
}

/*  BookListGenerator类,初始化时用一个[Book]?可选数组。
    关联类别名是Book; 方法next()返回Book或者nil。*/
class BookListGenerator: GeneratorType{
    var bookList:[Book]?
    var currentIndex:Int = 0
    
    init(bookList:[Book]?){
        self.bookList = bookList
    }
    
    typealias Element = Book
    func next() -> Element? {
        guard let list = self.bookList else{return nil}
        
        if self.currentIndex < list.count{
            let element = list[self.currentIndex]
            currentIndex++
            return element
        }else{
            return nil
        }

    }
}

/*  BookList类,初始化为一[Book]空数组。
    关联类别名是BookListGenerator, 方法generate返回BookListGenerator。*/
class BookList{
    var bookList:[Book]?
    
    init(){
        self.bookList = [Book]()
    }
    
    func addBook(book: Book){
        self.bookList?.append(book)
    }
    
    func removeBookAtIndex(index: Int){
        self.bookList?.removeAtIndex(index)
    }
}

extension BookList: SequenceType{
    typealias Generator = BookListGenerator
    func generate() -> Generator {
        return BookListGenerator(bookList: self.bookList)
    }
}

我们来试一下for…in枚举方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var book1 = Book(name: "c", price: 12.0)
var book2 = Book(name: "iOS", price: 18.0)
var book3 = Book(name: "Swift", price: 24.0)

var customizedBookList = BookList()
customizedBookList.addBook(book1)
customizedBookList.addBook(book2)
customizedBookList.addBook(book3)


for book in customizedBookList{
    print("Book-------name: \(book.name); price \(book.price)")
}
/*  输出为:
    Book-------name: c; price 12.0
    Book-------name: iOS; price 18.0
    Book-------name: Swift; price 24.0
*/

2.1.2 GeneratorType protocol 反向遍历

实现了正向遍历,反向遍历就很简单了,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class BookListGenerator: GeneratorType{
...
    typealias Element = Book
    func next() -> Element? {
        guard let list = self.bookList else{return nil}
        
        if self.currentIndex < list.count{
            let element = list[list.count-self.currentIndex-1]
            currentIndex++
            return element
        }else{
            return nil
        }
    }
...
}

同样的例子,输出如下:

1
2
3
4
5
6
7
8
for book in customizedBookList{
    print("Book-------name: \(book.name); price \(book.price)")
}
/*  输出为:
    Book-------name: Swift; price 24.0
    Book-------name: iOS; price 18.0
    Book-------name: c; price 12.0
*/

2.1.3 AnyGenerator: GeneratorType

如果每次实现定制类的for…in遍历都需要自己实现GeneratorType的类很不方便,Swift提供了一内置的生成器AnyGenerator。 我对AnyGenerator的定义如下:

AnyGenerator(任何生成器): 用一个全局函数尾随闭包来解决下一个生成”哪个”“类”的实例的问题的内置生成器。解释: 生成AnyGenerator的方法是, func anyGenerator(body: () -> Element?) -> AnyGenerator 是一个全局函数,输入是() -> Element?类型的函数,与next()作用一样,输出是AnyGenerator

我们来看看如何改写2.1

1
2
3
4
5
6
7
8
9
10
11
...
extension BookList: SequenceType{
    typealias Generator = AnyGenerator<Book>
    func generate() -> Generator {
        self.currentIndex = 0
        return anyGenerator(){ self.currentIndex < self.bookList?.count ? self.bookList![self.currentIndex++] : nil}
    }
/*  func anyGenerator<Element>(body: () -> Element?) -> AnyGenerator<Element>
    是一个全局函数,输入是() -> Element?类型的函数,与next()作用一样,输出是AnyGenerator<Element>*/
}
...

这样我们就不需要自己建一个新的Generator的子类了,非常方便。

2.2 SequenceType: map, filter, reduce

SequenceType 默认实现了很多高阶函数,例如map, filter, reduce等,因此实现SequenceType的类就可以用这些函数,举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//map 函数计算每本书的进价,返回数组
var boughtPrice = customizedBookList.map{return $0.price * 0.8}
print(boughtPrice)
/*  输出为:
    [9.6, 14.4, 19.2]
*/

//filter函数返回价格<20的Book数组
var lowPriceBooks = customizedBookList.filter{$0.price < 20}
for book in lowPriceBooks{
    print("Book-------name: \(book.name); price \(book.price)")
}
/*  输出为:
    Book-------name: c; price 12.0
    Book-------name: iOS; price 18.0
*/

/*reduce函数返回价格最高的Book,这里初始值应该是customizedBookList[0],
由于其还没有实现Indexable接口,因此不能用下标取其元素,我们用book1替代*/
var highestPriceBook: Book = customizedBookList.reduce(book1){
    $0.price > $1.price ? $0: $1
}
print("Highest Price Book-------name: \(highestPriceBook.name); price \(highestPriceBook.price)")
/*  输出为:
    Highest Price Book-------name: Swift; price 24.0
*/

3 Indexable 实现下标索引

我对其定义如下

Indexable(可索引): 定义了下标取值的接口。解释: 两个属性一个方法,startIndex是索引起点,endIndex是索引终点+1; subscript(position: Int) ->Book定义了下标方法。

code见下

1
2
3
4
5
6
7
8
9
10
11
extension BookList: Indexable{
    var startIndex: Int { return 0 }
    var endIndex: Int {
        guard let list = self.bookList else{ return 0 }
        return list.count
    }
    
    subscript(position: Int) ->Book?{
        return (startIndex ..< endIndex).contains(position) ? self.bookList?[position]: nil
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var book1 = Book(name: "c", price: 12.0)
var book2 = Book(name: "iOS", price: 18.0)
var book3 = Book(name: "Swift", price: 24.0)

var customizedBookList = BookList()
customizedBookList.addBook(book1)
customizedBookList.addBook(book2)
customizedBookList.addBook(book3)


print(bookList.endIndex)
print(bookList.startIndex)
for(var i = 0; i < customizedBookList.endIndex; i++){
    print("\(customizedBookList[i]!.name)")
}
/*  输出:
    3
    0
    c
    iOS
    Swift
*/

4 总结

SequenceType是序列类,生成了for…in的便捷遍历方法。其为生成器类的包装,有一个生成器类的工厂方法。 生成器类解决了下一个生成”哪个”“类”的实例的问题。SequenceType可以生成自定义继承自GeneratorType的类,也可以用anyGnerator()的Swift全局方法定义一个AnyGenerator类(方便于不用每次自定义)。GeneratorType里的next()方法让我们自定义如何取下一个实例(如正向,反向,Range作为array下标,无穷Range等)。同时实现了`SequenceType`接口的类可以享用其默认实现了的高阶函数, 如`map`,`filter`,`reduce`等。

Indexable定义了下标取值的接口。startIndex是索引起点,endIndex是索引终点+1; subscript(position: Int) ->Book定义了下标方法。

SequenceType + Indexable = CollectionType。有很多Swift内置类实现了CollectionType,如Array, Dictionary, Set等


Share Post

Twitter Google+

Shunmian

The only programmers in a position to see all the differences in power between the various languages are those who understand the most powerful one.