Combine-Subscriber

Publisher 根據(jù) Subscriber 的請求提供數(shù)據(jù)。如果沒有任何訂閱請求霎匈,Publisher 不會提供任何數(shù)據(jù)。所以可以這樣說,Subscriber負(fù)責(zé)向 Publisher 請求數(shù)據(jù)并接收數(shù)據(jù)(或失斂涿恕)。

Subscriber定義

public protocol Subscriber: CustomCombineIdentifierConvertible {

    /// 可以接收的數(shù)據(jù)的類型
    associatedtype Input

    /// 可以接收的錯誤類型像捶;如果不接收錯誤上陕,則使用 `Never`
    associatedtype Failure: Error

    /// Publisher調(diào)用此方法以提供訂閱
    func receive(subscription: Subscription)

    /// Publisher調(diào)用此方法發(fā)布新的數(shù)據(jù)
    func receive(_ input: Self.Input) -> Subscribers.Demand

    /// Publisher調(diào)用此方法發(fā)送錯誤或完成事件
    func receive(completion: Subscribers.Completion<Self.Failure>)
}

其中 InputFailure分別表示了 Subscriber 能夠接受的數(shù)據(jù)類型和錯誤類型桩砰,如果不接收錯誤,則使用Never释簿。

Subscription

連接 Publisher 和 Subscriber 是 Subscription亚隅,其定義如下:

public protocol Subscription: Cancellable, CustomCombineIdentifierConvertible {
    ///  告訴 Publisher 可以發(fā)送多少個數(shù)據(jù)到 Subscriber
    func request(_ demand: Subscribers.Demand)
}

Back pressure
Combine 約定 Subscriber 控制數(shù)據(jù)流,因此它可以同時控制整個流程中發(fā)生的所有操作庶溶,這個特性稱之為Back pressure煮纵。Subscription 中的request方法就體現(xiàn)了這種特性,它返回值是一個Subscribers.Demand偏螺,設(shè)置接受數(shù)據(jù)的最大值行疏,但是在每次收到新的數(shù)據(jù)以后都可以調(diào)整這個值,且這個值是累加的套像。

發(fā)布與訂閱流程

發(fā)布與訂閱流程.png
  1. Subscriber 通過調(diào)用Publisher.subscribe來告訴 Publisher 開始訂閱酿联。
  2. Publisher 通過調(diào)用Subscriber.receive(subscription:)發(fā)送確認(rèn)信息給 Subscriber。這個方法接收一個 Subscription凉夯。
  3. Subscriber 通過調(diào)用 2 中創(chuàng)建的 Subscription 上的request(_: Demand)方法來首次告訴 Publisher 需要事件的事件的最大值货葬。
  4. Publisher 通過調(diào)用Subscriber.·receive(_: Input)發(fā)送 1 個數(shù)據(jù)或者事件給 Subscriber 。
  5. 同4
  6. Publisher 通過調(diào)用Subscriber.receive(completion :)向 Subscriber 發(fā)送 completion 完成事件劲够。這里的 completion 可以是正常.finished震桶,也可以是.failure的,如果是.failure的會攜帶一個錯誤信息征绎。注意:如果中途取消了訂閱蹲姐,Publisher 將不發(fā)送完成事件。

自定義

自己實(shí)現(xiàn)一個 Subscriber人柿,寫完以后對 Publisher 和 Subscriber 之間的關(guān)系會更加明晰柴墩。

// 1 通過數(shù)組創(chuàng)建一個Publisher
let publisher = [1,2,3,4,5,6].publisher

// 2 自定義一個Subscriber
class CustomSubscriber: Subscriber {
    // 3 指定接收值的類型和錯誤類型
    typealias Input = Int
    typealias Failure = Never

    // 4 Publisher首先會調(diào)用該方法
    func receive(subscription: Subscription) {
        // 接收訂閱的值不做限制,也可以通過.max()設(shè)置最大值
        subscription.request(.unlimited)
    }

    // 5 接受到值時的方法凫岖,返回接收值的最大個數(shù)變化
    func receive(_ input: Int) -> Subscribers.Demand {
        // 打印出接收到的值
        print("Received value", input)
        // 返回.none,意思就是不改變最大接收數(shù)量江咳,也可以通過.max()設(shè)置增大多少
        return .none
    }

    // 6 實(shí)現(xiàn)接收到完成事件的方法
    func receive(completion: Subscribers.Completion<Never>) {
        print("Received completion", completion)
    }
}
// 訂閱Publisher
publisher.subscribe(CustomSubscriber())

/*輸出
Received value 1
Received value 2
Received value 3
Received value 4
Received value 5
Received value 6
Received completion finished
 */

內(nèi)置Subscriber

  • Sink
  • Assign

Sink

在閉包中處理數(shù)據(jù)或 completion 事件。

// 1 Just發(fā)送單個數(shù)據(jù)
let publisher = Just(1)
// 2 sink訂閱
publisher.sink(receiveCompletion: { _ in
    print("receiveCompletion")
}, receiveValue: { value in
    print(value)
})

/* 輸出
 1
 receiveCompletion
 */

Assign

  • 屬性寫入數(shù)據(jù)哥放。
  • 它接受一個class對象以及對象類型上的某個KeyPath歼指。會將 Publisher 的 Output 數(shù)據(jù)設(shè)置到對應(yīng)的屬性上去。
// 1 創(chuàng)建對象
class Student {
    var name: String = ""
}
let stu = Student()

// 2 Just發(fā)送單個數(shù)據(jù)
let publisher = Just("Hello Combine")
// 3 assign訂閱甥雕,設(shè)置到foo的bar屬性上
publisher.assign(to: \.name, on: stu)

print(stu.name)

/* 輸出
Hello Combine
*/

Cancellable

Combine 中提供了Cancellable這個協(xié)議踩身,里面只定義了一個cancel方法,用于提前結(jié)束訂閱流程社露。SinkAssign都實(shí)現(xiàn)了Cancellable 協(xié)議挟阻,所以可以調(diào)用cancel方法來取消訂閱。另外 Combine 中還定義了AnyCancellable類,它也實(shí)現(xiàn)了 Cancellable 協(xié)議附鸽,這個類會在deinit時自動執(zhí)行cancel方法脱拼。

protocol Cancellable {
    func cancel()
}

應(yīng)用

場景一:模擬用戶取消上傳數(shù)據(jù)。

let request = URLRequest(url: URL(string: "https://xxxxx")!)
let image = UIImage(named: "largeImage")
let imgFile: Data = image!.pngData()!

// 上傳Publisher
let downloadPublisher = Future<Data?, Never> { promise in
    URLSession.shared.uploadTask(with: request, from: imgFile) { (data, _, _) in
        promise(.success(data))
    }.resume()
}

// 訂閱
let subscription = downloadPublisher.sink { data in
    print("Received data: \(data)")
}

// 可以在完成之前調(diào)用cancel取消任務(wù)
subscription.cancel()

場景二:模擬網(wǎng)絡(luò)原因?qū)е碌木W(wǎng)絡(luò)請求中斷拒炎。

let dataPublisher = URLSession.shared.dataTaskPublisher(for: URL(string: "https://www.baidu.com")!)

let cancellableSink = dataPublisher
    .sink(receiveCompletion: { completion in
        switch completion {
        case .finished:
            print("received finished")
            break
        case .failure(let error):
            print("received error: ", error)
        }}, receiveValue: { someValue in
            print(".sink() received \(someValue)")
    })

// 可以取消
cancellableSink.cancel()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末挪拟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子击你,更是在濱河造成了極大的恐慌玉组,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丁侄,死亡現(xiàn)場離奇詭異惯雳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)鸿摇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門石景,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拙吉,你說我怎么就攤上這事潮孽。” “怎么了筷黔?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵往史,是天一觀的道長。 經(jīng)常有香客問我佛舱,道長椎例,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任请祖,我火速辦了婚禮订歪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肆捕。我一直安慰自己刷晋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布慎陵。 她就那樣靜靜地躺著掏秩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荆姆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天映凳,我揣著相機(jī)與錄音胆筒,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛仆救,可吹牛的內(nèi)容都是我干的抒和。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼彤蔽,長吁一口氣:“原來是場噩夢啊……” “哼摧莽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起顿痪,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤镊辕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蚁袭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體征懈,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年揩悄,在試婚紗的時候發(fā)現(xiàn)自己被綠了卖哎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡删性,死狀恐怖亏娜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蹬挺,我是刑警寧澤维贺,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站汗侵,受9級特大地震影響幸缕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜晰韵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一发乔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧雪猪,春花似錦栏尚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至官觅,卻和暖如春纵菌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背休涤。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工咱圆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留笛辟,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓序苏,卻偏偏與公主長得像手幢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子忱详,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容