創(chuàng)建Publisher 以及對其進行訂閱
新建Playground盏求,添加工具方法用于輸出示例代碼的運行情況
public func example(of description: String, action: () -> Void) {
print("\n——— Example of:", description, "———")
action()
}
首先在swift標準庫中我們可以使用如下方式發(fā)送和監(jiān)聽一個通知
/// 原始使用Notification的示例
example(of: "Notification") {
let myNotification = Notification.Name(rawValue: "MyNotification")
let observer = NotificationCenter.default.addObserver(forName: myNotification, object: nil, queue: nil) { _ in
print("receive notification")
}
NotificationCenter.default.post(name: myNotification, object: nil, userInfo: nil)
NotificationCenter.default.removeObserver(observer)
}
/*輸出:
——— Example of: Notification ———
receive notifacation
*/
同樣我們使用Combine后也可以對通知進行[圖片上傳中...(截屏2020-03-03下午2.51.48.png-66e51d-1583218332269-0)]
訂閱
/// 使用Combine管理Notification的例子
import Combine
example(of: "Combine") {
let myNotification = Notification.Name(rawValue: "MyNotification")
//創(chuàng)建一個publisher
let publisher = NotificationCenter.default.publisher(for: myNotification, object: nil)
//創(chuàng)建一個訂閱
let subscription = publisher.sink { _ in
print("receive notification")
}
NotificationCenter.default.post(name: myNotification, object: nil, userInfo: nil)
//結(jié)束訂閱
subscription.cancel()
}
/*輸出:
——— Example of: Combine ———
receive notifacation
*/
sink
注意以上代碼中的skin方法施敢,與其本意下沉似乎沒有什么關(guān)系,我們在Xcode中查看其源代碼聲明:
extension Publisher where Self.Failure == Never {
/// 用閉包添加一個subscriber
///
/// 此方法會創(chuàng)建一個 subscriber 并且立即訂閱 and immediately requests an unlimited number of values, prior to returning the subscriber.
/// 返回一個可取消的實例仑扑;用于在結(jié)束對接收值的使用時刪除訂閱流。
public func sink(receiveValue: @escaping ((Self.Output) -> Void)) -> AnyCancellable
}
以上我們可以利用skin創(chuàng)建一個subscriber了愕掏,同時我們也發(fā)現(xiàn)skin還有另一個定義
Just
example(of: "Just") {
let just = Just("Hello world!")
_ = just.sink(
receiveCompletion: {
print("Received completion", $0)
},
receiveValue: {
print("Received value", $0)
})
}
/*輸出
——— Example of: Just ———
Received value Hello world!
Received completion finished
*/
skin的這種訂閱可以分別訂閱到完成事件以及發(fā)出的值事件
Just
和RXSwift中一樣會創(chuàng)建一個只發(fā)出發(fā)出一個元素和一個完成事件的publisher滓侍。
assign(to:on:)
除了skin之外狈醉,內(nèi)置的assign(to:on:)操作符還允許您將接收到的值分配給對象的KVO屬性。
example(of: "assign(to:on:)") {
class SomeObject {
// 使用具有打印新值的didSet屬性觀察者的屬性定義類惠险。
var value: String = "" {
didSet { print(value)}
}
}
// 創(chuàng)建該類的實例舔糖。
let object = SomeObject()
// 從字符串數(shù)組創(chuàng)建發(fā)布服務(wù)器。
let publisher = ["Hello", "world!"].publisher
// publisher將接收到的每個值分配給對象的value屬性莺匠。
_ = publisher.assign(to: \.value, on: object)
}
/*輸出
——— Example of: assign(to:on:) ———
Hello
world!
*/
Cancellable
當訂閱publisher完成并且不再希望從publisher接收值時,最好取消訂閱以釋放資源并停止任何相應(yīng)的活動十兢,例如網(wǎng)絡(luò)調(diào)用趣竣。
訂閱返回anyCancelable
的實例作為“取消令牌”,這樣在完成訂閱后就可以取消訂閱旱物。
anyCancelable符合Cancelable協(xié)議遥缕,因此可以使用cancel()方法取消訂閱。
例如在 example(of: "Combine")
例子中的subscription.cancel()
就取消了對消息中心的訂閱和removeObserver(observer)
起到同樣的效果宵呛。如果不調(diào)用cancel()
那么直到publisher發(fā)出完成事假单匣,訂閱才會取消
注意:也可以忽略訂閱的返回值(例如,_=just.sink…)宝穗。但是有一個警告:如果您不在整個項目中存儲訂閱户秤,則一旦程序流退出其創(chuàng)建的范圍(方法結(jié)束后訂閱對象銷毀,訂閱結(jié)束)逮矛,該訂閱將被取消鸡号!
Publisher
我們在Xcode中點擊跳轉(zhuǎn)到Publisher的定義
public protocol Publisher {
/// 發(fā)送的值的類型
associatedtype Output
/// Publisher可能產(chǎn)生的錯誤類型;
/// 如果保證Publisher不會產(chǎn)生錯誤须鼎,則使用`Never`鲸伴。
associatedtype Failure : Error
/// 在調(diào)用 Publisher的`subscribe(_:)`方法時方法內(nèi)部會調(diào)用此方法去附加`Subscriber`
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}
extension Publisher {
/// 將指定的subscriber附加到此publisher
/// 調(diào)用此方法附加而不是`receive(subscriber:)`.
public func subscribe<S>(_ subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}
還有關(guān)聯(lián)的 Output和Failure一致是才能附加subscriber到publisher
Subscriber
public protocol Subscriber : CustomCombineIdentifierConvertible {
/// 可以接收的值的類型
associatedtype Input
/// 可以接收的錯誤類型;如果不接收錯誤晋控,則使用 `Never`
associatedtype Failure : Error
/// 調(diào)用此方法以提供訂閱
func receive(subscription: Subscription)
/// 發(fā)送剛發(fā)布的新值
func receive(_ input: Self.Input) -> Subscribers.Demand
/// 發(fā)送錯誤或完成事件
func receive(completion: Subscribers.Completion<Self.Failure>)
}
Subscription
連接publisher 和 subscriber 是 subscription. 下面是 Subscription protocol的定義
public protocol Subscription : Cancellable, CustomCombineIdentifierConvertible {
/// 告訴 publisher 可以發(fā)送多少個值到 subscriber.
func request(_ demand: Subscribers.Demand)
}
subscriber說明其愿意接收多少值的概念稱為
backpressure
汞窗。如果沒有它或其他策略,subscriber可能會收到來自publisher的超出其處理能力的大量值赡译,這可能會導(dǎo)致問題仲吏。在后面我們會詳細介紹,你也可以瀏覽王巍 (@onevcat)的這篇文章關(guān)于 Backpressure 和 Combine 中的處理
注意Subscriber
中的func receive(_ input: Self.Input) -> Subscribers.Demand
方法捶朵,返回值是一個Subscribers.Demand
蜘矢,即使訂閱最開始時設(shè)置了接受的最大值,我們在每次收到新的值都可以調(diào)整收到的最大值综看,這個值是累加的品腹,并且傳遞負值時會直接返回 fatalError,所以這個值只會越來越大而不能減少它
自定義subscriber
example(of: "Custom Subscriber") {
// 1 通過整數(shù)范圍創(chuàng)建一個publisher
let publisher = (1...6).publisher
// 2 自定義一個整型的Subscriber實現(xiàn)協(xié)議
final class IntSubscriber: Subscriber {
// 3 指定接收值的類型和錯誤類型
typealias Input = Int
typealias Failure = Never
// 4 實現(xiàn)協(xié)議方法 publisher會調(diào)用該方法
func receive(subscription: Subscription) {
//簡單實現(xiàn)為接收訂閱的值最多三個
subscription.request(.max(3))
}
// 5 實現(xiàn)接受到值時的方法红碑,返回接收值的最大個數(shù)變化
func receive(_ input: Int) -> Subscribers.Demand {
//這里簡單實現(xiàn)為打印值舞吭,返回 .none,意思就是不改變最大接收數(shù)量 .none 等價于 .max(0).
print("Received value", input)
return .none
}
// 6 實現(xiàn)接收到完成事件的方法
func receive(completion: Subscribers.Completion<Never>) {
print("Received completion", completion)
}
}
// 訂閱publisher
publisher.subscribe(IntSubscriber())
}
/*輸出
——— Example of: Custom Subscriber ———
Received value 1
Received value 2
Received value 3
*/
這里在一開始指定了只接受3個值(.max(3)
)并且在后續(xù)接受到新值時沒有更改最大值(.none
)泡垃,因此直接受到publisher發(fā)出的前三個值。如果我們將一開始的.none
改成.unlimited
或者接受到新值后我們每次加一最大值(.max(1)
)那么我們就會收到所有值羡鸥,并且收到完成事件
輸出結(jié)果應(yīng)該會是:
——— Example of: Custom Subscriber ———
Received value 1
Received value 2
Received value 3
Received value 4
Received value 5
Received value 6
Received completion finished
Future
就像使用Just創(chuàng)建一個publisher一樣蔑穴,F(xiàn)uture可以用于異步生成一個值,然后完成惧浴。
我們通過Xcode查看Future的定義
final public class Future<Output, Failure> : Publisher where Failure : Error {
public typealias Promise = (Result<Output, Failure>) -> Void
public init(_ attemptToFulfill: @escaping (@escaping Future<Output, Failure>.Promise) -> Void)
final public func receive<S>(subscriber: S) where Output == S.Input, Failure == S.Failure, S : Subscriber
}
Future
是一個 publisher
會異步產(chǎn)生一個值然后完成或者失敗存和,Promise
是閉包的類型別名,它接收一個Reuslt包含輸出的值或者失敗
Result類型為Swift5新增內(nèi)容衷旅,你可以查看Swift5新特性 & XCode 10.2更新獲取更多信息