RxSwift 文档:Error Handling
Error Handling - 错误处理
一旦序列里面产出了一个 error
事件,整个序列将被终止。RxSwift 主要有两种错误处理机制:
- retry - 重试
- catch - 恢复
retryWhen
如果请求 JSON
失败,我们需要等待5秒后进行重试操作,重试4次如果全部失败则抛出错误:
1 | // 请求 JSON 失败时,等待 5 秒后重试, |
一旦序列里面产出了一个 error
事件,整个序列将被终止。RxSwift 主要有两种错误处理机制:
如果请求 JSON
失败,我们需要等待5秒后进行重试操作,重试4次如果全部失败则抛出错误:
1 | // 请求 JSON 失败时,等待 5 秒后重试, |
AnyObserver
可以用来描叙任意一种观察者。
例如:
打印网络请求结果:
1 | URLSession.shared.rx.data(request: URLRequest(url: url)) |
函数响应式编程是种编程范式。它是通过构建函数操作数据序列,然后对这些序列做出响应的编程方式。它结合了函数式编程以及响应式编程。
函数式编程是种编程范式,它需要我们将函数作为参数传递,或者作为返回值返还。我们可以通过组合不同的函数来得到想要的结果。
函数试编程的优点:
1 | // 假设用户在进入页面到离开页面期间,总共点击按钮 3 次 |
Swift 的协议和 Objective-C 的协议不同。Swift 协议可以被用作代理,也可以让你对接口进行抽象 (比如 IteratorProtocol
和 Sequence
)。它们和 Objective-C 协议的最大不同在于我们可以让结构体和枚举类型满足协议。除此之外,Swift 协议还可以有关联类型。我们还可以通过协议扩展的方式为协议添加方法实现。我们会在面向协议编程的部分讨论所有这些内容。
协议允许我们进行动态派发,也就是说,在运行时程序会根据消息接收者的类型去选择正确的方法实现。
在面向对象编程中,子类是在多个类之间共享代码的有效方式。不过在 Swift 中,Sequence
中的代码共享是通过协议和协议扩展来实现的。通过这么做,Sequence
协议和它的扩展在结构体和枚举这样的值类型中依然可用,而这些值类型是不支持子类继承的。
协议扩展是一种可以在不共享基类的前提下共享代码的方法。协议定义了一组最小可行的方法集合,以供类型进行实现。而类型通过扩展的方式在这些最小方法上实现更多更复杂的特性。
这个动画是用 UICollectionView
实现的,简单讲下 UICollectionView
的工作原理。这里用到的 UICollectionView
也就3部分:ViewController
(简称VC)、UICollectionViewCell
、UICollectionViewLayout
。
ViewController
:
在VC里,UICollectionView
的用法跟 UITableView
的用法类似。这里的初始化方法与 UITableview
有所不同,多了个 collectionViewLayout
属性,每个 collectionView
都会绑定一个 UICollectionViewLayout
对象, collectionView
根据这个 layout
对象来布局 cell
。
UICollectionViewCell
:
这里用的 Cell
实现起来和 UITableViewCell
没什么大区别,我们只要实现它的 initwithFrame
的初始化方法即可,然后实现你想要的布局。
和大多数先进语言一样,Swift 拥有不少能被归类于泛型编程下的特性。使用泛型代码,你可以写出可重用的函数和数据结构,只要它们满足你所定义的约束,它们就能够适用于各种类型。比如,像是 Array
和 Set
等多个类型,实际上是它们中的元素类型就是泛型抽象。我们也可以创建泛型方法,它们可以对输入或者输出的类型进行泛型处理。func identity<A>(input: A) -> A
就定义了一个可以作用于任意类型 A 的函数。某种意义上,我们甚至可以认为带有关联类型的协议是“泛型协议”。关联类型允许我们对特定的实现进行抽象。IteratorProtocol
协议就是一个这样的例子:它所生成的 Element
就是一个泛型。
泛型编程的目的是表达算法或者数据结构所要求的核心接口。比如,考虑内建集合一章中的 last(where:)
函数。将它写为 Array
的一个扩展原本是最明显的选择,但是 Array
其实包含了很多 last(where:)
并不需要的特性。通过确认核心接口到底是什么,也就是说,找到想要实现的功能的最小需求,我们可以将这个函数定义在宽阔得多的类型范围内。在这个例子中,last(where:)
只有一个需求:它需要能够逆序遍历一系列元素。所以,将这个算法定义为 Sequence
的扩展是更好的选择 (我们也可以为 BidirectionalCollection
添加一个更高效的实现)。
Swift 支持重载操作符的特性,让我们可以自定义一些简单的计算。
最经典的例子就是两个二维向量之间的计算了。
首先我们定义一个二维向量,并创建两个向量
1 | struct Vector2D { |
相加两个向量:
1 | let v3 = Vector2D(x: v1.x + v2.x, y: v1.y + v2.y) |
这样一次的话,感觉还好。但是遇到复杂的运算的话,这样写感觉就太啰嗦了,这时候重载操作符是最好的选择。
1 | func +(left: Vector2D, right: Vector2D) -> Vector2D { |
大部分编程语言使用整数值对字符串进行下标操作,比如 str[5]
将会返回 str
中的第六个“字符” (这里的“字符”的概念由所操作的编程语言进行定义)。Swift 不允许这么做。为什么?答案可能现在你已经很耳熟了:因为整数的下标访问无法在常数时间内完成 (对于 Collection
协议来说这也是个直观要求),而且查找第 n 个 Character
的操作也必须要对它之前的所有字节进行检查。
String.Index
是 String
和它的视图所使用的索引类型,它本质上是一个存储了从字符串开头的字节偏移量的不透明值。如果你想计算第 n 个字符所对应的索引,你依然从字符串的开头或结尾开始,并花费 O(n) 的时间。但是一旦你拥有了有效的索引,就可以通过索引下标以 O(1) 的时间对字符串进行访问了。至关重要的是,通过一个已有索引来寻找下一个索引也是很快的,因为你可以从这个已有索引的字节偏移量开始进行查找,而不需要从头开始。正是由于这个原因,按顺序 (前向或者后向) 对字符串中的字符进行迭代是一个高效操作。