Combine 与响应式编程

Jul 16, 2022 • 预计阅读时间 2 分钟

什么是响应式编程?

简单的理解就是:仅当事件发生以后才响应处理

c <= a + b + c...

以上表达式中,c 是响应处理的逻辑,a, b, c 这些都是事件,当它们发生以后就会触发执行 c

使用术语表达,c订阅者Subscriber);a, b, c 这些都叫发布者Publisher); 另外还有对事件进行处理的逻辑,比如进行变换、去重等等,叫操作符Operator)。

完整的响应框架就由这三部分组成:订阅者Subscriber),发布者Publisher)和操作符Operator)。

举个例子

UI 事件总是在发生着,比如 UIView 中的安全区域会随着屏幕方向不同而变化,程序里需要根据安全区域的变化来调整布局。

使用响应式编程的思想来处理,就是订阅事件然后进行处理:

safeAreaSubscriber = view.publisher(for: \.safeAreaInsets).removeDuplicates().sink {
                         print($0)
                     }

sink 就是订阅者,负责响应处理逻辑,返回的是类型是 AnyCancellable,如果需要取消订阅就调用它的 cancel() 方法。 在 Combine 框架中,订阅者是需要保存起来的,否则就会被自动释放。

removeDuplicates() 就是操作符,它的作用是对事件进行去重,保证订阅者每次得到的数据都是不同的。

publisher(for: \.safeAreaInsets) 很明显就是发布者了,\.safeAreaInsets 是写法是 KeyPath 表达式:\{type-name}.{path}

使用 Combine 把命令式改造成函数式

申请相册访问权限的一般方式:

PHPhotoLibrary.requestAuthorization { status in
    DispatchQueue.main.async {
        if status == .authorized {
            // 访问相册的逻辑代码
        } else {
            // 提示用户没有权限访问相册
        }
    }
}

改为响应式的方式,把相册的申请调用调整为返回一个发布者类型:

enum PhotosAuthError: Error {
    case notAuthorized(PHAuthorizationStatus)
}

func authorizeAccessPhotos() -> AnyPublisher<PHAuthorizationStatus, PhotosAuthError> {
    return Future { promise in
        PHPhotoLibrary.requestAuthorization { status in
            if status == .authorized {
                promise(.success(status))
            } else {
                promise(.failure(PhotosAuthError.notAuthorized(status)))
            }
        }
    }.eraseToAnyPublisher()
}

使用的时候:

private var cancellables = Set<AnyCancellable>()

authorizeAccessPhotos().receive(on: DispatchQueue.main)
    .sink(receiveCompletion: {
            // 没有访问权限
            if case .failure(let error) = $0 {
                if case .notAuthorized(let status) = error {
                    print(status)
                }
            }
        }, receiveValue: {
            // 获取到了访问权限
            print($0)
        })
    .store(in: &cancellables)

代码比原来更多了,但是会带来一些额外的好处:对事件可以添加更多的控制逻辑。

与观察者模式的差异

KVO、KVC 是观察者模式的典型代表,也是监听变更然后进行逻辑处理。

观察者模式不能对事件进行变换、筛选等操作,没有操作符

响应式编程是把事件抽象为时间流上的点,订阅者订阅这个时间流上发生的事件,然后进行汇总处理。

其它第三方响应式编程库

因为 Combine 框架是 iOS 13 才提供的,而且是为了配合 SwiftUI 而诞生的。 在这之前已经有了很多第三方开源库实现了响应式编程框架。虽然每个库里订阅者Subscriber),发布者Publisher)和操作符Operator) 有不同的名称,但是设计模式是一致的,只要稍加学习就可以很快上手使用。

iOS
版权声明:如果转发请带上本文链接和注明来源。

lvv.me

iOS/macOS Developer

如何使用代码结束 iOS 应用

C++ 中的右值引用