有些时候我们想要随时在Publisher插入值来通知订阅者(Rx中也提供了Subject类型实现)。
Subject通常是一个中间代理,既可以作为Publisher,也可以作为Observer。
官方给出的定义如下:
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public protocol Subject<Output, Failure> : AnyObject, Publisher {
/// Sends a value to the subscriber.
///
/// - Parameter value: The value to send.
func send(_ value: Self.Output)
/// Sends a completion signal to the subscriber.
///
/// - Parameter completion: A `Completion` instance which indicates whether publishing has finished normally or failed with an error.
func send(completion: Subscribers.Completion<Self.Failure>)
/// Sends a subscription to the subscriber.
///
/// This call provides the ``Subject`` an opportunity to establish demand for any new upstream subscriptions.
///
/// - Parameter subscription: The subscription instance through which the subscriber can request elements.
func send(subscription: Subscription)
}
- 作为Subscriber时,可以通过Publisher的subscribe(_:Subject)方法订阅某个Publisher。
- 作为Publisher时,可以通过send方法,在数据流中随时插入数据。目前在Combine中,有三个已经实现的Subject:AnySubject,CurrentValueSubject和PassthroughSubject。
利用Subject可以很轻松地将现在已有的代码的一部分转成Reactive。
import UIKit
import Combine
class SubjectViewController: UIViewController {
let cm2 = CbContentManager()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .cyan
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// 方式1
// let cm = ContentManager(delegate: self, content: .red)
// cm.getContent()
// 方式2
let _ = cm2.content.sink {
print("comp: \($0)")
} receiveValue: {
print("receive: \($0)")
self.view.backgroundColor = $0
}
cm2.getContent()
}
}
extension SubjectViewController: ContentManagerDelegate {
func contentDidChange(_ content: UIColor) {
view.backgroundColor = content
}
}
//MARK: - 方式一 代理响应
protocol ContentManagerDelegate {
func contentDidChange(_ content: UIColor)
}
class ContentManager: NSObject {
var delegate: ContentManagerDelegate
var content: UIColor {
didSet {
delegate.contentDidChange(content)
}
}
init(delegate: ContentManagerDelegate, content: UIColor) {
self.delegate = delegate
self.content = content
}
func getContent() {
content = UIColor(red: CGFloat(arc4random()%256)/255.0, green: CGFloat(arc4random()%256)/255.0, blue: CGFloat(arc4random()%256)/255.0, alpha: CGFloat(arc4random()%6)/5.0+0.5)
}
}
//MARK: - 方式2 响应
class CbContentManager {
var content = CurrentValueSubject<UIColor, NSError>(.orange)
func getContent() {
content.value = UIColor(red: CGFloat(arc4random()%256)/255.0, green: CGFloat(arc4random()%256)/255.0, blue: CGFloat(arc4random()%256)/255.0, alpha: CGFloat(arc4random()%6)/5.0+0.5)
content.send(UIColor(red: CGFloat(arc4random()%256)/255.0, green: CGFloat(arc4random()%256)/255.0, blue: CGFloat(arc4random()%256)/255.0, alpha: CGFloat(arc4random()%6)/5.0+0.5))
}
}
CurrentValueSubject就是包含一个初始值,并且会在每次值变化的时候发送一个消息,这个值会保存,可以很方便的用来替换Property Observer。
PassthroughSubject和CurrentValueSubject几乎一样,只是没有初始值,也不会保存任何值。