数组
- 数组和可变性
// 斐波那契数列
let fibs = [0,1,1,2,3,5]
不可变 不能用append(_:)
var fibs = [0,1,1,2,3,5]
let 只能保证引用永远不发生变化,而不能保证引用的值发生变化
标准库里的集合类型都是有值语义的
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 是引用类型
- 数组和可选值
标准库的提供的 first last popLast 不会数组越界 而是 返回 Optional 它总会执行边界检查 - Map Filter ....
- 数组切片
数组切片不是Array,只是数组的一种表示方式,但是可以当做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
}
}
}
// 这样就可以合并字典了 而且参数可以是键值对数组或任意类似序列
- Hashable 要求
字典其实是哈希表,字典通过键 hashValue 来确定值的位置
标准库中所有类型都支持 Hashable 要求
Set
无序且不重复
- 执行集合代数 SetAlgebra 协议
Set 是标准库中唯一实现了 SetAlgebra 协议的类型,而Foundation中有另外的IndexSet,CharacterSet 他们已经被Swift以值的方式导入了
Range
范围代表两个值之间的区域let singleDigitNumbers = 0..<10, let lowercaseLetters = Character('a')...Character('z')
范围看起来是一个序列或集合类型,但它并非两者之一。
| 半开范围 | 闭合范围
------- |----------|---------
Comparable | Range | ClosedRange
Strideable | CountableRange | CountableClosedRange
以整数为步长
- 只有半开范围能表达空间区的概念
- 只有闭合范围能包含元素类型的最大值
0...Int.max
半开范围总是最少有一个值比范围所表达的值要大Int.max + 1 ???
- 只有那些可数的区域才能 for in 遵循Sequence协议
集合类型协议 太难下次再讲😅
可选值
- 岗哨值
编程语言中有一种常见模式,那就是操作是否要返回一个有效的值
int ch;
while ((ch=getchar()) != EOF) {
printf("Read character %c\n", ch);
}
printf("Reached end-of-file %c\n", ch);
- EOF 是对 -1 的 #define 它代表未找到 或者 空值
在各种语言中 null 最为常见 - 函数返回一个代表并没有返回真实的值的值,被称为 岗哨值
- 它容易被忽略检测而产生错误
let s: NSString? = "..."
if s.range(of: "swift").location != NSNotFound {
print("someone mentioned sift!")
}
如果someString 是 nil 将返回一个都是0的range 语句将会被执行
- 通过枚举解决问题 swift 的枚举可包含另外的关联值
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
- 和岗哨值不通 除非显示的检测解包否则你不可能意外使用到一个Optional中的值
- index(of:) 就会返回一个Optional<Index>的值 它遵守ExpressibleByNilLiteral协议 可以写作 Index? 用 nil 代替.none
- 如果你要使用返回的值 就必须解包
- if let
- while let
在 Swift 中 for in 其实就是 where let 来实现的 避免了闭包的值捕获问题
a = []
for i in 1..3 {
a.push(lambda{i})
}
for f in a {
print "#{f.call()}"
}
输出 ???
- 双重可选值 Optional<Optional<Int>> 系统会自动解包一层
- if var and while var
- guard 可以 扩大 可选作用域
- 可选链 OC 中nil 不会发消息 执行方法 swift中 ?可选链实现了同样的效果
- 合并运算符 ??
- 可选值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?] 是不可以的 因为 == 需要数组元素遵守Equable 协议 - 改进强制解包错误信息
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)\""
崩溃前执行
- 隐式可选值
var s: String! = "s"
在行为上它表现的像是非可选值,但它还可以使用可选值 的可选链 if let 等
集合类型协议
在之前,我们看到了 Array Dictionary 和 Set,它们并非空中楼阁,而是建立在一系列由 Swift 标准库提供的用于处理元素序列的抽象之上的。
Sequence 和 collection协议,它们构成了这套集合类型模型的基石。本章会研究这些协议是如何工作的,它们为什么要以这样的方式工作,以及如何写出自己的序列和集合类型等
- 序列
Sequence协议是集合序列的基础,代表的是一系列具有相同类型的值,你可以对这些值进行迭代。遍历一个序列最简单的方式是使用for循环
for element in someSequence {
doSomething(with: element)
}
Sequence协议提供了许多强大的功能,满足该协议的类型都可以直接使用这些功能。上面这样步进式地迭代元素的能力看起来十分简单,但它却是 Sequence可以提供这些强大功能的基础。在上一章中已经提到过不少这类功能了,每当你遇到一个能够针对元素序列进行的通用的操作,你都应该考虑将它实现在 Sequence层可能性。在接下来的部分,会看到许多这方面的例子。
满足 Sequence 协议非常简单 只需要提供一个返回迭代器iterator
的 makeIterator()
方法:
protocol Sequence {
associatedtype Iterator: IteratorProtocol
func makeIterator()->Iterator
}
对于迭代器,我们现在只能从 Sequence 的定于中看出他满足 IteratorProtocol
协议的类型。所以首先来仔细看看迭代器是什么。
- 迭代器
序列通过一个迭代器来访问元素IteratorProtocol
协议中有一个next()
方法返回序列中下一个元素的值直到序列耗尽返回nil
protocol IteratorProtocol {
associatedtype Element
mutating func next() -> Element?
}
本质上for循环就是下面代码的简写 当然我们也可创造无限序列
var iterator = someSequence.makeIterator()
while let element = iterator.next() {
doSomething(with: element)
}
next 被标记mutating 看起来是不需要的 但实践中迭代器本质是存在状态的
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
}
}
基于函数的迭代器序列
func uniqueIntegerProvider() -> AnyIterator<Int> {
var i = 0
return AnyIterator {
i += 1
return i
}
}
let prodiver = AnySequence(uniqueIntegerProvider)
Array(prodiver.prefix(10))
无限序列, sequence 的 next 闭包总是延时执行的 也就是说 下一个next 不会被自动计算 prodiver.prefix(10) 只会求前10个
如果序列主动计算 它就会溢出崩溃
对集合和序列来说 区别之一 就是 序列可以无限 而集合不行
不稳定序列 网络流 UI时间流 磁盘文件 都可以用序列建模
网络包这种序列将被遍历消耗再次遍历不能保证是同样的值,
sequence 明确指出了不保证可以被多次遍历
- 集合类型 Collection
是指那些稳定的序列 能够被多次遍历并保存一致,可以下标访问 有起始和终止索引。
Collection协议是基于Sequence来的除了继承所有方法外,它还可以获取指定位置的元素 获取 稳定的迭代的保证,count等新的特性
- (1)实现一个队列 (2)遵守ExpressibleByArrayLiteral 协议 (3)关联类型
// 实现一个队列
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] = []
}
// 实现了这些 就能满足Collection协议了
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]
}
}
// 我们要实现的有
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
// 扩展 新的协议
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:)
实现一个单向链表 (非整数索引)