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
}
}
}
代碼稍長,請聽我給大家一一分解~
-
首先,GeneratorType 定義了一個屬性別名: typealias Element。 我們將 Book 類賦值給它,表示我們這個集合中存儲的數據類型是 Book 類的實例。
-
接下來,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
}
}
- next 方法中,先用 guard 關鍵字進行了一次判斷,檢查 bookList(也就是實際的數據是否為空),如果為空,就直接返回 nil。 宣告遍歷結束~
- 接下來,用了一個叫做 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 語言特性知識的內容,還可以看一看這幾篇內容:
最后,感謝大家花了這么長時間把這篇文章看完。希望給大家提供更多有價值的內容,期待大家的寶貴意見。