Day16 Combine 03 - Subscriber

Subscriber

与Publisher 相对应,是观察者模式中的Observer,Publisher在自身状态改变时,调用Subscriber 的三个不同方法receive(subscription)receive(_:Input)receive(completion:)来通知Subscriber

Subscriber 订阅Publisher 後会返回一个遵循Cancellable协定的AnyCancellable,作用上类似於其他响应式框架中的dispose,控制者订阅者的释放,在开发中可以将其作为属性栏位,在页面销毁时,调用AnyCancellable内部的cancel()方法进行资源释放

以下举一些常见的Subscriber 例子:

  1. sink

    Sink是非常通用的Subscriber,我们可以自由的处理数据流的状态,Publisher 还提供了extension funcsink(receiveCompletion:, receiveValue:)方法来直接订阅,为订阅者连结基於闭包的行为

    let integers = (0...3)
    integers.publisher
        .sink { print("Received \($0)") }
    

    参数:

    • receiveComplete: 在完成时执行的闭包
    • receiveValue: 在收到值时执行的闭包
    let integers = (0...3)
    integers.publisher
        .sink(receiveCompletion:{print("Completion: \($0)")},
        receiveValue:{ print("Value: \($0)")})
    
  2. Subject

    Subject 是一种 Publisher,你可以调用其 send(_:) 方法将值注入到数据流中,很适合将已有的程序改造成兼容 Combine,有些时候我们想随时在Publisher 插入值来通知订阅者,在Rx 中也提供了一个Subject类型来实现。Subject 通常是一个中间代理,即可以作为Publisher,也可以作为Observer

    • 作为Observer的时候,可以通过Publisher的subscribe(_:Subject)方法订阅某个Publisher
    • 作为Publisher的时候,可以主动通过Subject的两个send方法,我们可以在数据流中随时插入数据

    目前在Combine 中,有三个已经实现对Subject: AnySubjectCurrentValueSubjectPassthroughSubject

    范例:

    // Before
    class ContentManager {
        var content: [String] {
            didSet {
                delegate?.contentDidChange(content)
            }
        }
        func getContent() {
            content = ["hello", "world"]
        }
    }
    
    // After
    class ContentController {
        var content = CurrentValueSubject<[String], NSError>([])
        func getContent() {
            content.value = ["hello", "world"]
        }
    }
    

    CurrentValueSubject的功能很简单,就是包含一个初始值,并且会在每次值变化的时候发送一个消息,这个值会被保存,可以很方便的用来替代Property Observer。在上例中,以前需要实现delegate 来获取变化,现在只需要订阅content 的变化即可,并且它作为一个Publisher,可以很方便的利用操作符进行组合变换

    PassthroughSubjectCurrentValueSubject几乎一样,只是没有初始值,也不会保存任何值

  3. Assign

    Assign可以很方便地将接收到的值通过KeyPath设置到指定的Class 上(不支持Struct),很适合将已有的程序改造成Reactive,将来自Publisher 的每个元素分配给物件中的属性

    参数:

    • keyPath:表示要分配属性的 keyPath。关於 Key-Path 表达式请参考此官方文章
    • object:包含此属性的物件。订阅者在每次接收到新值时都会分配给该物件的属性
    class Student {
        let name: String
        var score: Int
    
        init(name: String, score: Int) {
            self.name = name
            self.score = score
        }
    }
    
    let student = Student(name: "Ryder", score: 91)
    print(student.score)
    let observer = Subscribers.Assign(object: student, keyPath: \Student.score)
    let publisher = PassthroughSubject<Int, Never>()
    publisher.subscribe(observer)
    publisher.send(95)
    print(student.score)
    publisher.send(98)
    print(student.score)
    

    一旦publisher 的值发生改变,对应的studentscore也会被更新


<<:  Day 19动画的封装与简化

>>:  javascript(event&DOM)(DAY18)

Day12-D3 的 Tooltips

本篇大纲:tooltips 基础设定、tooltips 进阶应用 今天我们要来讲解算是D3最轻松简...

D22 - 「不断线的侏罗纪」:很久很久以前的侏罗纪

本篇来实际建立游戏场景! 建立游戏场景 建立游戏场景组件 game-scene.vue,并提供「跳跃...

Day 23. Zabbix 通知设定 - Custom alertscripts - Line

在 SMTP Mail 之後,今天要跟大家介绍第二种通知方式 Custom alertscripts...

Day31 ATT&CK for ICS - Inhibit Response Function(3)

T0814 Denial of Service 攻击者为了破坏设备的功能,使用阻断服务的攻击,会在短...

伸缩自如的Flask [day11] log with mongoDB

首先,理论上今天应该进展使用Python到写资料进mySql,但是我发现用来记录log的套件像是lo...