Swift 响应式编程详解
目录
- 响应式编程概述
- 响应式编程框架
- RxSwift 介绍
- RxSwift 核心角色
- Observable 和 Observer
- 创建 Observable
- 创建 Observer
- 事件处理
- 状态监听
- Disposable 管理
- 实际应用
- 最佳实践
- 总结
响应式编程概述
响应式编程(Reactive Programming,简称RP)是一种编程范式,于1997年提出,可以简化异步编程,提供更优雅的数据绑定。
什么是响应式编程?
响应式编程是一种面向数据流和变化传播的编程范式。它具有以下特点:
- 异步编程简化:提供更优雅的异步数据处理方式
- 数据绑定:支持数据的自动同步和更新
- 函数式融合:通常与函数式编程结合,称为函数响应式编程(FRP)
- 事件驱动:基于事件流的编程模型
响应式编程的优势
- 简洁的异步代码:避免回调地狱
- 统一的处理方式:用同一套API处理各种异步事件
- 更好的组合性:可以轻松组合各种操作
- 更少的状态管理:减少可变状态的复杂性
响应式编程框架
ReactiveCocoa (RAC)
ReactiveCocoa是Objective-C和Swift的响应式编程框架:
- 简称:RAC
- 支持语言:Objective-C、Swift
- 官网:reactivecocoa.io/
- GitHub:github.com/ReactiveCoc…
ReactiveX (Rx)
ReactiveX是跨语言的响应式编程框架:
- 简称:Rx
- 支持语言:Java、Kotlin、JavaScript、C++、PHP、Go、Swift等
- 官网:reactivex.io/
- GitHub:github.com/ReactiveX
ReactiveX 各语言版本
| 语言 | 框架名 | 描述 |
|---|---|---|
| Java | RxJava | Java平台的响应式编程 |
| Kotlin | RxKotlin | Kotlin的响应式编程 |
| JavaScript | RxJS | JavaScript的响应式编程 |
| C++ | RxCpp | C++的响应式编程 |
| Swift | RxSwift | Swift的响应式编程 |
RxSwift 介绍
RxSwift是ReactiveX的Swift版本,为Swift开发者提供了强大的响应式编程能力。
安装配置
CocoaPods 安装
1. Podfile 配置
use_frameworks!
target 'YourTargetName' do
pod 'RxSwift', '~> 5'
pod 'RxCocoa', '~> 5'
end
2. 命令行安装
pod repo update
pod install
3. 导入模块
import RxSwift
import RxCocoa
模块说明
| 模块 | 描述 |
|---|---|
| RxSwift | Rx标准API的Swift实现,不包括任何iOS相关的内容 |
| RxCocoa | 基于RxSwift,给iOS UI控件扩展了很多Rx特性 |
资源链接
RxSwift 核心角色
核心概念
RxSwift的核心包含以下角色:
- Observable:负责发送事件(Event)
- Observer:负责订阅Observable,监听Observable发送的事件
- Event:事件,包含数据或状态信息
- Operator:操作符,用于加工处理事件序列
事件类型
public enum Event<Element> {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
RxSwift中的事件有3种类型:
- next:携带具体数据
- error:携带错误信息,表明Observable终止,不会再发出事件
- completed:表明Observable终止,不会再发出事件
Observable 和 Observer
Observable(可观察序列)
Observable是RxSwift的核心,它代表一个可观察的序列。
// 基本概念
let observable = Observable<String>.create { observer in
observer.onNext("Hello")
observer.onNext("World")
observer.onCompleted()
return Disposables.create()
}
Observer(观察者)
Observer用于观察Observable发出的事件。
let observer = AnyObserver<String> { event in
switch event {
case .next(let value):
print("接收到值: \(value)")
case .error(let error):
print("发生错误: \(error)")
case .completed:
print("序列完成")
}
}
创建 Observable
基本创建方法
1. create 方法
let observable = Observable<Int>.create { observer in
observer.onNext(1)
observer.onNext(2)
observer.onNext(3)
observer.onCompleted()
return Disposables.create()
}
2. just 方法
// 发送单个值
let observable = Observable.just(1)
3. of 方法
// 发送多个值
let observable = Observable.of(1, 2, 3)
4. from 方法
// 从数组创建
let observable = Observable.from([1, 2, 3])
5. timer 方法
// 定时器
let observable = Observable<Int>.timer(
.seconds(3),
period: .seconds(1),
scheduler: MainScheduler.instance
)
高级创建方法
interval 方法
// 间隔发送
let observable = Observable<Int>.interval(
.seconds(1),
scheduler: MainScheduler.instance
)
range 方法
// 范围序列
let observable = Observable.range(start: 1, count: 5)
repeatElement 方法
// 重复元素
let observable = Observable.repeatElement("Hello")
创建 Observer
AnyObserver
let observer = AnyObserver<Int> { event in
switch event {
case .next(let data):
print("数据: \(data)")
case .completed:
print("完成")
case .error(let error):
print("错误: \(error)")
}
}
Observable.just(1).subscribe(observer).dispose()
Binder
let binder = Binder<String>(label) { label, text in
label.text = text
}
Observable.just("Hello")
.map { "值是: \($0)" }
.bind(to: binder)
.dispose()
扩展 Binder 属性
extension Reactive where Base: UIView {
var hidden: Binder<Bool> {
return Binder<Bool>(base) { view, value in
view.isHidden = value
}
}
}
// 使用自定义Binder
let observable = Observable<Int>.interval(
.seconds(1),
scheduler: MainScheduler.instance
)
observable
.map { $0 % 2 == 0 }
.bind(to: button.rx.hidden)
.disposed(by: bag)
事件处理
订阅事件
完整订阅
observable.subscribe(
onNext: { value in
print("next: \(value)")
},
onError: { error in
print("error: \(error)")
},
onCompleted: {
print("completed")
},
onDisposed: {
print("disposed")
}
).disposed(by: bag)
简单订阅
observable.subscribe { event in
print(event)
}.disposed(by: bag)
数据绑定
observable
.map { "数值是: \($0)" }
.bind(to: label.rx.text)
.disposed(by: bag)
状态监听
传统监听方案的问题
传统的状态监听方案包括:
- KVO
- Target-Action
- Notification
- Delegate
- Block Callback
这些方案经常出现错综复杂的依赖关系,耦合性较高,还需要编写重复的非业务代码。
RxSwift 的优势
1. 按钮点击事件
button.rx.tap.subscribe(onNext: {
print("按钮被点击了")
}).disposed(by: bag)
2. TableView 数据绑定
let data = Observable.just([
Person(name: "Jack", age: 10),
Person(name: "Rose", age: 20)
])
data.bind(to: tableView.rx.items(cellIdentifier: "cell")) { row, person, cell in
cell.textLabel?.text = person.name
cell.detailTextLabel?.text = "\(person.age)"
}.disposed(by: bag)
// 选中事件
tableView.rx.modelSelected(Person.self)
.subscribe(onNext: { person in
print("选中了: \(person.name)")
}).disposed(by: bag)
3. KVO 监听
class Dog: NSObject {
@objc dynamic var name: String?
}
let dog = Dog()
dog.rx.observe(String.self, "name")
.subscribe(onNext: { name in
print("name is \(name ?? "nil")")
}).disposed(by: bag)
dog.name = "Larry"
dog.name = "WangWang"
4. 通知监听
NotificationCenter.default.rx
.notification(UIApplication.didEnterBackgroundNotification)
.subscribe(onNext: { notification in
print("APP进入后台: \(notification)")
}).disposed(by: bag)
5. 双向绑定
// UISlider 和 UITextField 的双向绑定
slider.rx.value
.map { "当前数值是: \($0)" }
.bind(to: textField.rx.text)
.disposed(by: bag)
textField.rx.text
.subscribe(onNext: { text in
print("text is \(text ?? "nil")")
}).disposed(by: bag)
// 设置初始值
Observable.just(0.8)
.bind(to: slider.rx.value)
.dispose()
ControlProperty 特性
诸如 UISlider.rx.value、UITextField.rx.text 这类属性值既是Observable又是Observer,它们是 RxCocoa.ControlProperty 类型。
Disposable 管理
什么是 Disposable?
每当Observable被订阅时,都会返回一个Disposable实例。当调用Disposable的dispose方法时,就相当于取消订阅。
取消订阅的方式
1. 立即取消订阅
observable.subscribe { event in
print(event)
}.dispose()
2. 使用 DisposeBag
let disposeBag = DisposeBag()
observable.subscribe { event in
print(event)
}.disposed(by: disposeBag)
// 当 disposeBag 销毁时,会自动调用所有 Disposable 的 dispose 方法
3. 使用 takeUntil
let _ = observable
.takeUntil(self.rx.deallocated)
.subscribe { event in
print(event)
}
// 当 self 销毁时,会自动取消订阅
DisposeBag 的使用
class ViewController: UIViewController {
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
// 所有订阅都添加到 disposeBag
button.rx.tap
.subscribe(onNext: {
print("按钮被点击")
})
.disposed(by: disposeBag)
}
// 当 ViewController 被销毁时,disposeBag 也会被销毁
// 所有相关的订阅都会被自动取消
}
实际应用
1. 登录表单验证
class LoginViewController: UIViewController {
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var loginButton: UIButton!
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let usernameValid = usernameTextField.rx.text.orEmpty
.map { $0.count >= 3 }
.share(replay: 1)
let passwordValid = passwordTextField.rx.text.orEmpty
.map { $0.count >= 6 }
.share(replay: 1)
let everythingValid = Observable.combineLatest(
usernameValid,
passwordValid
) { $0 && $1 }
everythingValid
.bind(to: loginButton.rx.isEnabled)
.disposed(by: disposeBag)
loginButton.rx.tap
.subscribe(onNext: {
print("登录按钮被点击")
})
.disposed(by: disposeBag)
}
}
2. 网络请求
func fetchUserData() -> Observable<User> {
return Observable.create { observer in
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
observer.onError(error)
return
}
if let data = data {
do {
let user = try JSONDecoder().decode(User.self, from: data)
observer.onNext(user)
observer.onCompleted()
} catch {
observer.onError(error)
}
}
}
task.resume()
return Disposables.create {
task.cancel()
}
}
}
// 使用
fetchUserData()
.observeOn(MainScheduler.instance)
.subscribe(
onNext: { user in
print("用户信息: \(user)")
},
onError: { error in
print("错误: \(error)")
}
)
.disposed(by: disposeBag)
3. 搜索功能
searchTextField.rx.text.orEmpty
.throttle(.milliseconds(300), scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest { query -> Observable<[SearchResult]> in
if query.isEmpty {
return Observable.just([])
}
return searchAPI(query: query)
}
.observeOn(MainScheduler.instance)
.bind(to: tableView.rx.items(cellIdentifier: "cell")) { row, result, cell in
cell.textLabel?.text = result.title
}
.disposed(by: disposeBag)
最佳实践
1. 使用 DisposeBag 管理订阅
class ViewController: UIViewController {
private let disposeBag = DisposeBag()
// 所有订阅都应该添加到 disposeBag
}
2. 避免循环引用
// 错误的做法
observable.subscribe(onNext: { [unowned self] value in
self.updateUI(value)
}).disposed(by: disposeBag)
// 正确的做法
observable.subscribe(onNext: { [weak self] value in
self?.updateUI(value)
}).disposed(by: disposeBag)
3. 使用操作符简化代码
// 使用 map 转换数据
observable
.map { $0 * 2 }
.filter { $0 > 10 }
.subscribe(onNext: { value in
print(value)
})
.disposed(by: disposeBag)
4. 合理使用 share()
let sharedObservable = observable.share(replay: 1)
sharedObservable
.subscribe(onNext: { print("Observer 1: \($0)") })
.disposed(by: disposeBag)
sharedObservable
.subscribe(onNext: { print("Observer 2: \($0)") })
.disposed(by: disposeBag)
5. 错误处理
observable
.catchErrorJustReturn("默认值")
.subscribe(onNext: { value in
print(value)
})
.disposed(by: disposeBag)
总结
响应式编程为iOS开发带来了新的编程范式,RxSwift作为Swift的响应式编程框架,提供了强大而优雅的解决方案。
关键点回顾
- Observable 和 Observer 是RxSwift的核心概念
- DisposeBag 是内存管理的关键
- 操作符 提供了强大的数据处理能力
- 绑定 简化了UI更新逻辑
响应式编程的优势
- 统一的异步处理:用同一套API处理各种异步事件
- 声明式编程:关注数据流而不是控制流
- 减少状态管理:避免复杂的状态同步
- 更好的可测试性:纯函数式的操作更容易测试
学习建议
- 从简单开始:先掌握基本的Observable和Observer
- 实践操作符:通过实际项目学习各种操作符
- 理解内存管理:正确使用DisposeBag避免内存泄漏
- 循序渐进:逐步将现有项目迁移到响应式编程
注意:响应式编程有一定的学习曲线,建议从简单的场景开始,逐步掌握更复杂的用法。合理使用响应式编程能够让代码更加清晰和易维护。