基本概念

代码

import Combine

/* Combine包含三个主要角色:Publisher发布者、operater操作符和subscriberi订阅者。发布者用于发布数据,操作符对发布的数据进行加工,最后由订阅者处理发布的数据 */

// 将一个数组作为数据的发布者,遵循Publisher协议的对象,能够发送跟随时间变化的值序列
let sourcePublisher = [true,false,1,0,"publishedValue"].publisher

// 通过sink方法创建了一个subscriber,该subscriber在闭包中处理收到的数据。从底部的控制台可以看到:订阅者依次收到的来自发布者的数据序列
_ = sourcePublisher.sink { value in
    print(value)
}

let sourcePublisher2 = [true,false,1,0,"publishedValue"].publisher
let subscriber = sourcePublisher2.sink(receiveCompletion: { completion in
    switch completion
    {
        case .finished:
            print("receiveCompletion: Finished")
        case .failure(let never):
            print("receiveCompletion: \(never)")
    }
}, receiveValue: { value in
    print(value)
})
// 调用订阅者的cancel方法,取消这个订阅,避免内存泄漏的情况。订阅取消之后,发布者也会停止发布数据
subscriber.cancel()

截图

iShot20220410 下午4.37.09.png

使用Passthrough Subject

代码

import Combine

// 初始化一个PassthroughSubject发布者,并设定发布的数据的类型,错误类型为Error
let passthroughSubject = PassthroughSubject<Int,Error>()

// 通过sink方法生成一个订阅者,当订阅者接收到发布者发布的数据时,立刻在控制台打印这条数据
let subscribe = passthroughSubject
    .filter {$0 > 0}
    .map {$0 * 10}
    .scan(0) { seed, value in
        return seed + value
    }
    .sink(receiveCompletion: { _ in}, receiveValue: { value in
        print("\(value)")
    })
// 当需要向订阅者发布一条数据时用发布者的send方法即可,此时订阅者subscriber接收到这条数据,并将它输出到控制台上
passthroughSubject.send(10)
passthroughSubject.send(-10)
passthroughSubject.send(10)
passthroughSubject.send(10)

截图

iShot20220410 下午4.50.29.png

PassthroughSubject发布状态

代码

import Combine

// 首先定义一个遵守Error协议的枚举,它拥有一个成员,表示出现了分数的值小于0的错误状况
enum ScoreError: Error
{
    case negativeNumber
}
// 初始化一个PassthroughSubject发布者,并设定发布的数据的类型的整型,错误类型为ScoreError
let passthroughSubject = PassthroughSubject<Int,ScoreError>()
// 通过sink方法生成一个订阅者,并通过receiveCompletion闭包,处理当订阅者完成对数据的接收时的事件
let subscriber = passthroughSubject
    .sink(receiveCompletion: { (completion) in
        // 对数据接收完成事件的状态进行判断
        switch completion
        {
            case .finished:
                    print("receiveCompletion: Finished")
            case .failure(let never):
                print("receiveCompletion: \(never)")
        }
    }, receiveValue: { value in
        print("\(value)")
    })

passthroughSubject.send(1)
passthroughSubject.send(2)
passthroughSubject.send(3)
// 当向订阅者发送的completion为finished时,会调用订阅者的receiveCompletion闭包,此时在控制台输出了相应的日志
passthroughSubject.send(completion: .finished)

截图

iShot20220410 下午5.01.00.png

使用CurrentValueSubject

代码

import Combine

// CurrentValueSubject是另一种Subject,PassthroughSubject相比,订阅者不仅可以获得未来发布的所有事件,还可以获得订阅之前的最后一个事件
// 首先定义一个遵守Errort协议的枚举,它拥有一个成员,表示出现了分数的值小于0的错误状况
enum ScoreError: Error
{
    case negativeNumber
}
// 初始化一个PassthroughSubject发布者,我们先使用这个Subject,以方便和CurrentValueSubject进行对比
//let subject = PassthroughSubject<Int,ScoreError>()
let subject = CurrentValueSubject<Int,ScoreError>(0)
//subject.send(1)

let subscriber2 = subject
    .sink(receiveCompletion: { _ in }, receiveValue: { value in
        print("\(value)")
    })

// 调用发布者的send方法,向订阅者发布两条数据。从控制台输出的日志可以看出,订阅者接收到了两条数据,订阅之前发布的数据并没有接收到
subject.send(2)
subject.send(3)

截图

iShot20220410 下午5.08.05.png
将Passthrough Subject修改为CurrentValueSubject,从控制台输出的日志可以看出,订阅者已经接收到了订阅之前发布的数据。

iShot20220410 下午5.10.30.png

如果注释掉第13行的代码,订阅者依然收到一条值为0的数据,这是因为我们在第12行代码,对CurrentValueSubject初始化时,发布了一条值为0的数据

iShot20220410 下午5.11.32.png

Q.E.D.