數(shù)組
- 數(shù)組和可變性
// 斐波那契數(shù)列
let fibs = [0,1,1,2,3,5]
不可變 不能用append(_:)
var fibs = [0,1,1,2,3,5]
let 只能保證引用永遠不發(fā)生變化,而不能保證引用的值發(fā)生變化
標準庫里的集合類型都是有值語義的
let x = [1,2,3]
var y = x
y.append(4)
// x 123
// y 1234
let z = NSMutableArray(array: [1,2,3])
z.insert(4, at:3)
//z 是引用類型
- 數(shù)組和可選值
標準庫的提供的 first last popLast 不會數(shù)組越界 而是 返回 Optional 它總會執(zhí)行邊界檢查 - Map Filter ....
- 數(shù)組切片
數(shù)組切片不是Array,只是數(shù)組的一種表示方式,但是可以當做Array使用
let slice = fibs[1...<fibs.endIndex]
type(of: slice) // ArraySlice<Int>
字典
字典是無序的,字典是一種稀疏結構不能保證某個鍵下的值是否有值
- 可變性
- 擴展merge
extension Dictionary {
mutating func merge<S>(_ other: S) where S: Sequence, S.Iterator.Element == (key: Key, value: Value) {
for (k, v) in other {
self[k] = v
}
}
}
// 這樣就可以合并字典了 而且參數(shù)可以是鍵值對數(shù)組或任意類似序列
- Hashable 要求
字典其實是哈希表,字典通過鍵 hashValue 來確定值的位置
標準庫中所有類型都支持 Hashable 要求
Set
無序且不重復
- 執(zhí)行集合代數(shù) SetAlgebra 協(xié)議
Set 是標準庫中唯一實現(xiàn)了 SetAlgebra 協(xié)議的類型,而Foundation中有另外的IndexSet,CharacterSet 他們已經(jīng)被Swift以值的方式導入了
Range
范圍代表兩個值之間的區(qū)域let singleDigitNumbers = 0..<10, let lowercaseLetters = Character('a')...Character('z')
范圍看起來是一個序列或集合類型,但它并非兩者之一。
| 半開范圍 | 閉合范圍
------- |----------|---------
Comparable | Range | ClosedRange
Strideable | CountableRange | CountableClosedRange
以整數(shù)為步長
- 只有半開范圍能表達空間區(qū)的概念
- 只有閉合范圍能包含元素類型的最大值
0...Int.max半開范圍總是最少有一個值比范圍所表達的值要大Int.max + 1 ??? - 只有那些可數(shù)的區(qū)域才能 for in 遵循Sequence協(xié)議
集合類型協(xié)議 太難下次再講??
可選值
- 崗哨值
編程語言中有一種常見模式,那就是操作是否要返回一個有效的值
int ch;
while ((ch=getchar()) != EOF) {
printf("Read character %c\n", ch);
}
printf("Reached end-of-file %c\n", ch);
- EOF 是對 -1 的 #define 它代表未找到 或者 空值
在各種語言中 null 最為常見 - 函數(shù)返回一個代表并沒有返回真實的值的值,被稱為 崗哨值
- 它容易被忽略檢測而產(chǎn)生錯誤
let s: NSString? = "..."
if s.range(of: "swift").location != NSNotFound {
print("someone mentioned sift!")
}
如果someString 是 nil 將返回一個都是0的range 語句將會被執(zhí)行
- 通過枚舉解決問題 swift 的枚舉可包含另外的關聯(lián)值
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
- 和崗哨值不通 除非顯示的檢測解包否則你不可能意外使用到一個Optional中的值
- index(of:) 就會返回一個Optional<Index>的值 它遵守ExpressibleByNilLiteral協(xié)議 可以寫作 Index? 用 nil 代替.none
- 如果你要使用返回的值 就必須解包
- if let
- while let
在 Swift 中 for in 其實就是 where let 來實現(xiàn)的 避免了閉包的值捕獲問題
a = []
for i in 1..3 {
a.push(lambda{i})
}
for f in a {
print "#{f.call()}"
}
輸出 ???
- 雙重可選值 Optional<Optional<Int>> 系統(tǒng)會自動解包一層
- if var and while var
- guard 可以 擴大 可選作用域
- 可選鏈 OC 中nil 不會發(fā)消息 執(zhí)行方法 swift中 ?可選鏈實現(xiàn)了同樣的效果
- 合并運算符 ??
- 可選值map
let characters = ["a","b"]
let string = String(characters.first)
編譯失敗
可以寫成
let string = characters.first.map{ String($0) }
- flatMap
let numbers = ["1","foo"]
let x = numbers.first.map{ Int($0) } //Optional<Optional<Int>>
let x = numbers.first.flatMap{ Int($0) } //Optional<Int>
- 比較 我們可以 寫出代碼
if numbers.first == "1"而不需要加Optional("1")然而 [Int?] == [Int?] 是不可以的 因為 == 需要數(shù)組元素遵守Equable 協(xié)議 - 改進強制解包錯誤信息
func !! <T>(wrapped: T?, failureText: @autoclosure ()-> String) -> T {
if let x = wrapped {
return x
}
fatalError(failureText())
}
let ss = "foo"
let i = Int(ss) !! "nonono, get \"\(ss)\""
崩潰前執(zhí)行
- 隱式可選值
var s: String! = "s"
在行為上它表現(xiàn)的像是非可選值,但它還可以使用可選值 的可選鏈 if let 等
集合類型協(xié)議
在之前,我們看到了 Array Dictionary 和 Set,它們并非空中樓閣,而是建立在一系列由 Swift 標準庫提供的用于處理元素序列的抽象之上的。
Sequence 和 collection協(xié)議,它們構成了這套集合類型模型的基石。本章會研究這些協(xié)議是如何工作的,它們?yōu)槭裁匆赃@樣的方式工作,以及如何寫出自己的序列和集合類型等
- 序列
Sequence協(xié)議是集合序列的基礎,代表的是一系列具有相同類型的值,你可以對這些值進行迭代。遍歷一個序列最簡單的方式是使用for循環(huán)
for element in someSequence {
doSomething(with: element)
}
Sequence協(xié)議提供了許多強大的功能,滿足該協(xié)議的類型都可以直接使用這些功能。上面這樣步進式地迭代元素的能力看起來十分簡單,但它卻是 Sequence可以提供這些強大功能的基礎。在上一章中已經(jīng)提到過不少這類功能了,每當你遇到一個能夠針對元素序列進行的通用的操作,你都應該考慮將它實現(xiàn)在 Sequence層可能性。在接下來的部分,會看到許多這方面的例子。
滿足 Sequence 協(xié)議非常簡單 只需要提供一個返回迭代器iterator的 makeIterator()方法:
protocol Sequence {
associatedtype Iterator: IteratorProtocol
func makeIterator()->Iterator
}
對于迭代器,我們現(xiàn)在只能從 Sequence 的定于中看出他滿足 IteratorProtocol協(xié)議的類型。所以首先來仔細看看迭代器是什么。
- 迭代器
序列通過一個迭代器來訪問元素IteratorProtocol協(xié)議中有一個next()方法返回序列中下一個元素的值直到序列耗盡返回nil
protocol IteratorProtocol {
associatedtype Element
mutating func next() -> Element?
}
本質(zhì)上for循環(huán)就是下面代碼的簡寫 當然我們也可創(chuàng)造無限序列
var iterator = someSequence.makeIterator()
while let element = iterator.next() {
doSomething(with: element)
}
next 被標記mutating 看起來是不需要的 但實踐中迭代器本質(zhì)是存在狀態(tài)的
struct FibsIterator: IteratorProtocol {
typealias Element = Int
var state = (0, 1)
mutating func next() -> Int? {
let upcomingNumber = state.0
state = (state.1, state.0 + state.1)
return upcomingNumber
}
}
基于函數(shù)的迭代器序列
func uniqueIntegerProvider() -> AnyIterator<Int> {
var i = 0
return AnyIterator {
i += 1
return i
}
}
let prodiver = AnySequence(uniqueIntegerProvider)
Array(prodiver.prefix(10))
無限序列, sequence 的 next 閉包總是延時執(zhí)行的 也就是說 下一個next 不會被自動計算 prodiver.prefix(10) 只會求前10個
如果序列主動計算 它就會溢出崩潰
對集合和序列來說 區(qū)別之一 就是 序列可以無限 而集合不行
不穩(wěn)定序列 網(wǎng)絡流 UI時間流 磁盤文件 都可以用序列建模
網(wǎng)絡包這種序列將被遍歷消耗再次遍歷不能保證是同樣的值,
sequence 明確指出了不保證可以被多次遍歷
- 集合類型 Collection
是指那些穩(wěn)定的序列 能夠被多次遍歷并保存一致,可以下標訪問 有起始和終止索引。
Collection協(xié)議是基于Sequence來的除了繼承所有方法外,它還可以獲取指定位置的元素 獲取 穩(wěn)定的迭代的保證,count等新的特性
- (1)實現(xiàn)一個隊列 (2)遵守ExpressibleByArrayLiteral 協(xié)議 (3)關聯(lián)類型
// 實現(xiàn)一個隊列
protocol Queue {
associatedtype Element // self持有的類型
mutating func enquenue(_ element: Element) // 入隊
mutating func dequenue() -> Element? // 出隊
}
struct FIFOQuenue<Element>: Queue {
mutating func enquenue(_ element: Element) { // 入隊
right.append(element)
}
mutating func dequenue() -> Element? { // 出隊
if left.isEmpty {
left = right.reversed()
right.removeAll()
}
return left.popLast()
}
fileprivate var left: [Element] = []
fileprivate var right: [Element] = []
}
// 實現(xiàn)了這些 就能滿足Collection協(xié)議了
extension FIFOQuenue: Collection {
func index(after i: Int) -> Int {
precondition(i < endIndex)
return i + 1
}
subscript(position: Int) -> Element {
precondition((0..<endIndex).contains(position), "Index out of bounds")
if position < left.endIndex {
return left[left.count - position - 1]
} else {
return right[position - left.count]
}
}
// 我們要實現(xiàn)的有
public var startIndex: Int { return 0 }
public var endIndex: Int { return left.count + right.count }
}
var q = FIFOQuenue<String>()
for x in ["1", "2", "f00", "3"] {
q.enquenue(x)
}
for s in q {
print(s)
}
q.count
// 擴展 新的協(xié)議
extension FIFOQuenue: ExpressibleByArrayLiteral {
init(arrayLiteral elements: Element...) {
self.init(left: elements.reversed(), right: [])
}
typealias ArrayLiteralElement = Element
}
var qq = [1,2,3]
for ss in qq {
print(ss)
}
- 索引
startIndex 是第一個元素位置
endIndex 是最后一個元素之后的位置
通過索引訪問 subscript 是Collection定義的 它總是返回非可選值
索引失效,可能是指向了另一個元素 或者本身失效了 訪問可能導致崩潰
索引步進 index(after:)
實現(xiàn)一個單向鏈表 (非整數(shù)索引)