轩辕十四

探索科技与创新的个人博客

Error Handling - 错误处理


一旦序列里面产出了一个 error 事件,整个序列将被终止。RxSwift 主要有两种错误处理机制:

  • retry - 重试
  • catch - 恢复

retryWhen

如果请求 JSON 失败,我们需要等待5秒后进行重试操作,重试4次如果全部失败则抛出错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 请求 JSON 失败时,等待 5 秒后重试,
// 重试 4 次后仍然失败,就将错误抛出

let maxRetryCount = 4 // 最多重试 4 次
let retryDelay: Double = 5 // 重试延时 5 秒

rxJson
.retryWhen { (rxError: Observable<Error>) -> Observable<Int> in
return rxError.flatMapWithIndex { (error, index) -> Observable<Int> in
guard index < maxRetryCount else {
return Observable.error(error)
}
return Observable<Int>.timer(retryDelay, scheduler: MainScheduler.instance)
}
}
.subscribe(...)
.disposed(by: disposeBag)
阅读全文 »

Observer - 观察者


AnyObserver


AnyObserver 可以用来描叙任意一种观察者。

例如:

打印网络请求结果:

1
2
3
4
5
6
7
URLSession.shared.rx.data(request: URLRequest(url: url))
.subscribe(onNext: { data in
print("Data Task Success with count: \(data.count)")
}, onError: { error in
print("Data Task Error: \(error)")
})
.disposed(by: disposeBag)
阅读全文 »

Observable - 可被监听的序列


所有的事物都是序列

Observable 可以用于描述元素异步产生的序列。这样我们生活中许多事物都可以通过它来表示。

如何创建序列

创建序列最直接的方法就是调用 Observable.create,然后在构建函数里面描述元素的产生过程。 observer.onNext(0) 就代表产生了一个元素,他的值是 0。后面又产生了 9 个元素分别是 1, 2, ... 8, 9 。最后,用 observer.onCompleted() 表示元素已经全部产生,没有更多元素了。

阅读全文 »

Schedulers - 调度器


Schedulers 是 Rx 实现多线程的核心模块,它主要用于控制任务在哪个线程或队列运行。

如果你曾经使用过 GCD, 那你对以下代码应该不会陌生:

1
2
3
4
5
6
7
// 后台取得数据,主线程处理结果
DispatchQueue.global(qos: .userInitiated).async {
let data = try? Data(contentsOf: url)
DispatchQueue.main.async {
self.data = data
}
}
阅读全文 »

函数响应式编程


函数响应式编程是种编程范式。它是通过构建函数操作数据序列,然后对这些序列做出响应的编程方式。它结合了函数式编程以及响应式编程。

函数式编程

函数式编程是种编程范式,它需要我们将函数作为参数传递,或者作为返回值返还。我们可以通过组合不同的函数来得到想要的结果。

函数试编程的优点:

  • 灵活
  • 高复用
  • 简洁
  • 易维护
  • 适应各种需求变化

函数式编程 -> 函数响应式编程

1
2
3
4
5
6
7
// 假设用户在进入页面到离开页面期间,总共点击按钮 3 次

// 按钮点击序列
let taps: Array<Void> = [(), (), ()]

// 每次点击后弹出提示框
taps.forEach { showAlert() }
阅读全文 »

Swift 的协议和 Objective-C 的协议不同。Swift 协议可以被用作代理,也可以让你对接口进行抽象 (比如 IteratorProtocolSequence )。它们和 Objective-C 协议的最大不同在于我们可以让结构体和枚举类型满足协议。除此之外,Swift 协议还可以有关联类型。我们还可以通过协议扩展的方式为协议添加方法实现。我们会在面向协议编程的部分讨论所有这些内容。

协议允许我们进行动态派发,也就是说,在运行时程序会根据消息接收者的类型去选择正确的方法实现。

在面向对象编程中,子类是在多个类之间共享代码的有效方式。不过在 Swift 中,Sequence 中的代码共享是通过协议和协议扩展来实现的。通过这么做,Sequence 协议和它的扩展在结构体和枚举这样的值类型中依然可用,而这些值类型是不支持子类继承的。

协议扩展是一种可以在不共享基类的前提下共享代码的方法。协议定义了一组最小可行的方法集合,以供类型进行实现。而类型通过扩展的方式在这些最小方法上实现更多更复杂的特性。

阅读全文 »

实现效果


控件 — UICollectionView


这个动画是用 UICollectionView 实现的,简单讲下 UICollectionView 的工作原理。这里用到的 UICollectionView 也就3部分:ViewController(简称VC)、UICollectionViewCellUICollectionViewLayout

  1. ViewController
    在VC里,UICollectionView 的用法跟 UITableView 的用法类似。这里的初始化方法与 UITableview 有所不同,多了个 collectionViewLayout 属性,每个 collectionView 都会绑定一个 UICollectionViewLayout 对象, collectionView 根据这个 layout 对象来布局 cell

  2. UICollectionViewCell
    这里用的 Cell 实现起来和 UITableViewCell 没什么大区别,我们只要实现它的 initwithFrame 的初始化方法即可,然后实现你想要的布局。

阅读全文 »

和大多数先进语言一样,Swift 拥有不少能被归类于泛型编程下的特性。使用泛型代码,你可以写出可重用的函数和数据结构,只要它们满足你所定义的约束,它们就能够适用于各种类型。比如,像是 ArraySet 等多个类型,实际上是它们中的元素类型就是泛型抽象。我们也可以创建泛型方法,它们可以对输入或者输出的类型进行泛型处理。func identity<A>(input: A) -> A 就定义了一个可以作用于任意类型 A 的函数。某种意义上,我们甚至可以认为带有关联类型的协议是“泛型协议”。关联类型允许我们对特定的实现进行抽象。IteratorProtocol 协议就是一个这样的例子:它所生成的 Element 就是一个泛型。

泛型编程的目的是表达算法或者数据结构所要求的核心接口。比如,考虑内建集合一章中的 last(where:) 函数。将它写为 Array 的一个扩展原本是最明显的选择,但是 Array 其实包含了很多 last(where:) 并不需要的特性。通过确认核心接口到底是什么,也就是说,找到想要实现的功能的最小需求,我们可以将这个函数定义在宽阔得多的类型范围内。在这个例子中,last(where:) 只有一个需求:它需要能够逆序遍历一系列元素。所以,将这个算法定义为 Sequence 的扩展是更好的选择 (我们也可以为 BidirectionalCollection 添加一个更高效的实现)。

阅读全文 »

Swift 支持重载操作符的特性,让我们可以自定义一些简单的计算。
最经典的例子就是两个二维向量之间的计算了。

首先我们定义一个二维向量,并创建两个向量

1
2
3
4
5
6
struct Vector2D {
var x = 0.0
var y = 0.0
}
let v1 = Vector2D(x: 2.0, y: 3.0)
let v2 = Vector2D(x: 1.0, y: 4.0)

相加两个向量:

1
let v3 = Vector2D(x: v1.x + v2.x, y: v1.y + v2.y)

这样一次的话,感觉还好。但是遇到复杂的运算的话,这样写感觉就太啰嗦了,这时候重载操作符是最好的选择。

1
2
3
func +(left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
阅读全文 »

字符串索引


大部分编程语言使用整数值对字符串进行下标操作,比如 str[5] 将会返回 str 中的第六个“字符” (这里的“字符”的概念由所操作的编程语言进行定义)。Swift 不允许这么做。为什么?答案可能现在你已经很耳熟了:因为整数的下标访问无法在常数时间内完成 (对于 Collection 协议来说这也是个直观要求),而且查找第 n 个 Character 的操作也必须要对它之前的所有字节进行检查。

String.IndexString 和它的视图所使用的索引类型,它本质上是一个存储了从字符串开头的字节偏移量的不透明值。如果你想计算第 n 个字符所对应的索引,你依然从字符串的开头或结尾开始,并花费 O(n) 的时间。但是一旦你拥有了有效的索引,就可以通过索引下标以 O(1) 的时间对字符串进行访问了。至关重要的是,通过一个已有索引来寻找下一个索引也是很快的,因为你可以从这个已有索引的字节偏移量开始进行查找,而不需要从头开始。正是由于这个原因,按顺序 (前向或者后向) 对字符串中的字符进行迭代是一个高效操作。

阅读全文 »
0%