(十三)RxSwift之调度器(Schedulers )

1,068 阅读3分钟

网络请求是开发中必不可少的,因为请求是耗时的,所以我们常常进行异步操作,拿到数据以后回到主线线程刷新UI。

用RxSwift可以这样表示:

let rxData: Observable<Data> = ...

rxData
    .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
    .observeOn(MainScheduler.instance)
    .subscribe(onNext: { [weak self] data in
        self?.data = data
    })
    .disposed(by: disposeBag)
  • subscribeOn

subscribeOn 来决定数据序列的构建函数在哪个 Scheduler 上运行。由于获取 Data 为耗时操作,所以用 subscribeOn 切换到后台 Scheduler 来获取 Data。这样可以避免主线程被阻塞。

  • observeOn

observeOn 来决定在哪个 Scheduler 监听这个数据序列。例子中,通过使用 observeOn 方法切换到主线程来监听并且处理结果。

RxSwift中提供了几种常用的调度器:

  • MainScheduler

MainScheduler 代表主线程。如果需要执行一些和 UI 相关的任务,就需要切换到该 Scheduler 运行。

public final class MainScheduler : SerialDispatchQueueScheduler {

    private let _mainQueue: DispatchQueue

    let numberEnqueued = AtomicInt(0)

    /// Initializes new instance of `MainScheduler`.
    public init() {
        self._mainQueue = 'DispatchQueue.main'
        super.init(serialQueue: self._mainQueue)
    }

    /// Singleton instance of `MainScheduler`
    public static let instance = MainScheduler()
}

代码中非常明显的看到self._mainQueue = 'DispatchQueue.main初始化时绑定了主队列。

  • ConcurrentMainScheduler

这个schedulersubscibeOn操作符有优化,所以,如果希望element的产生在主线程上,那么用ConcurrentMainScheduler

public final class ConcurrentMainScheduler : SchedulerType {
    private let _mainScheduler: MainScheduler
    private let _mainQueue: DispatchQueue

    private init(mainScheduler: MainScheduler) {
        self._mainQueue = DispatchQueue.main  // 主队列
        self._mainScheduler = mainScheduler
    }
    /// Singleton instance of `ConcurrentMainScheduler`
    public static let instance = ConcurrentMainScheduler(mainScheduler: MainScheduler.instance)
}
  • SerialDispatchQueueScheduler

SerialDispatchQueueScheduler 抽象了串行 DispatchQueue。如果需要执行一些串行任务,可以切换到这个 Scheduler 运行它能够保证即使你传入的是一个并发的队列,它也会把他转化成串行的。

public class SerialDispatchQueueScheduler : SchedulerType {
    let configuration: DispatchQueueConfiguration
    
    init(serialQueue: DispatchQueue, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
        self.configuration = DispatchQueueConfiguration(queue: serialQueue, leeway: leeway)
    }

    public convenience init(internalSerialQueueName: String, serialQueueConfiguration: ((DispatchQueue) -> Void)? = nil, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
        let queue = DispatchQueue(label: internalSerialQueueName, attributes: [])
        serialQueueConfiguration?(queue)
        self.init(serialQueue: queue, leeway: leeway)
    }
}

SerialDispatchQueueScheduler为我们提供了多种便利构造器来初始化一个串行队列。

  • ConcurrentDispatchQueueScheduler

ConcurrentDispatchQueueScheduler抽象了并行 DispatchQueue。如果需要执行一些并发任务,可以切换到这个 Scheduler 运行。

public class ConcurrentDispatchQueueScheduler: SchedulerType {
    let configuration: DispatchQueueConfiguration
    
    public init(queue: DispatchQueue, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
        self.configuration = DispatchQueueConfiguration(queue: queue, leeway: leeway)
    }

    @available(iOS 8, OSX 10.10, *)
    public convenience init(qos: DispatchQoS, leeway: DispatchTimeInterval = DispatchTimeInterval.nanoseconds(0)) {
        self.init(queue: DispatchQueue(
            label: "rxswift.queue.\(qos)",
            qos: qos,
            attributes: [DispatchQueue.Attributes.concurrent],
            target: nil),
            leeway: leeway
        )
    }
}
  • OperationQueueScheduler

抽象了 NSOperationQueue

它具备 NSOperationQueue 的一些特点,可以通过设置优先级和最大并发任务数量(maxConcurrentOperationCount)。

public class OperationQueueScheduler: ImmediateSchedulerType {
    public let operationQueue: OperationQueue
    public let queuePriority: Operation.QueuePriority
    
    public init(operationQueue: OperationQueue, queuePriority: Operation.QueuePriority = .normal) {
        self.operationQueue = operationQueue
        self.queuePriority = queuePriority
    }
}
  • CurrentThreadScheduler

还有一个默认使用的调度器--当前调度器CurrentThreadScheduler

public class CurrentThreadScheduler : ImmediateSchedulerType {
    typealias ScheduleQueue = RxMutableBox<Queue<ScheduledItemType>>

    /// The singleton instance of the current thread scheduler.
    public static let instance = CurrentThreadScheduler()

    private static var 'isScheduleRequiredKey': pthread_key_t = { () -> pthread_key_t in
        let key = UnsafeMutablePointer<pthread_key_t>.allocate(capacity: 1)
        defer { key.deallocate() }
        
        guard pthread_key_create(key, nil) == 0 else {
            rxFatalError("isScheduleRequired key creation failed")
        }

        return key.pointee
    }()

    private static var scheduleInProgressSentinel: UnsafeRawPointer = { () -> UnsafeRawPointer in
        return UnsafeRawPointer(UnsafeMutablePointer<Int>.allocate(capacity: 1))
    }()

    static var 'queue' : ScheduleQueue? {
        get {
            return Thread.getThreadLocalStorageValueForKey(CurrentThreadSchedulerQueueKey.instance)
        }
        set {
            Thread.setThreadLocalStorageValue(newValue, forKey: CurrentThreadSchedulerQueueKey.instance)
        }
    }

    /// Gets a value that indicates whether the caller must call a `schedule` method.
    public static fileprivate(set) var 'isScheduleRequired': Bool {
        get {
            return pthread_getspecific(CurrentThreadScheduler.isScheduleRequiredKey) == nil
        }
        set(isScheduleRequired) {
            if pthread_setspecific(CurrentThreadScheduler.isScheduleRequiredKey, isScheduleRequired ? nil : scheduleInProgressSentinel) != 0 {
                rxFatalError("pthread_setspecific failed")
            }
        }
    }
}

isScheduleRequiredKey 是拿到线程当前的key

通过对queuesetget 方法来实现我们的当前队列与静态字符串的绑定。

extension Thread {
    static func setThreadLocalStorageValue<T: AnyObject>(_ value: T?, forKey key: NSCopying) {
        let currentThread = Thread.current
        let threadDictionary = currentThread.threadDictionary

        if let newValue = value {
            threadDictionary[key] = newValue
        }
        else {
            threadDictionary[key] = nil
        }
    }

    static func getThreadLocalStorageValueForKey<T>(_ key: NSCopying) -> T? {
        let currentThread = Thread.current
        let threadDictionary = currentThread.threadDictionary
        
        return threadDictionary[key] as? T
    }
}

isScheduleRequired表示当前队列是否被关联, 当前线程没有被关联时isScheduleRequired的返回值为true。 当isScheduleRequired设置为false时三目运算将scheduleInProgressSentinel赋值给线程的key,表示队列被关联。

SchedulerType description
MainScheduler 代表主线程。执行一些和 UI 相关的任务
ConcurrentMainScheduler 对subscibeOn操作符有优化,使望element的产生在主线程上
SerialDispatchQueueScheduler 抽象了串行队列,执行一些串行任务
ConcurrentDispatchQueueScheduler 抽象了并行 队列,执行一些并发任务
OperationQueueScheduler 它具备 NSOperationQueue 的一些特点,可以设置优先级和最大并发数
CurrentThreadScheduler 当前调度器,默认调度器

最后赋上一张调度器关系继承图: