RACUnit和RACScheduler

2019-04-03  本文已影响0人  boy丿log

RACUnit

RACUnit类中只有一个单例

@interface RACUnit : NSObject

/// A singleton instance.
+ (RACUnit *)defaultUnit;

@end

RACScheduler

RACScheduler 是一个线性执行队列,ReactiveCocoa 中的信号可以在 RACScheduler 上执行任务、发送结果;它的实现并不复杂,由多个简单的方法和类组成整个 RACScheduler 模块,是整个 ReactiveCocoa 中非常易于理解的部分。

先来看下头文件

首先是创建方法:

//返回一个同步执行的队列
+ (RACScheduler *)immediateScheduler;

//返回一个RACTargetQueueScheduler队列
+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(nullable NSString *)name;

//返回一个RACTargetQueueScheduler队列,默认名字为org.reactivecocoa.ReactiveObjC.RACScheduler.backgroundScheduler
+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority;

//返回一个主队列,是一个单例
+ (RACScheduler *)mainThreadScheduler;

//返回一个优先级为普通的队列
+ (RACScheduler *)scheduler;

//返回当先队列
+ (nullable RACScheduler *)currentScheduler;

然后是方法:

//当前队列异步执行方法
- (nullable RACDisposable *)schedule:(void (^)(void))block;
//在某个时间点后执行方法
- (nullable RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block;

//延时执行block
- (nullable RACDisposable *)afterDelay:(NSTimeInterval)delay schedule:(void (^)(void))block;
//封装了一个GCD定时器,参数意义分别是,1.第一次开始执行任务的时间,2.每个多少秒循环一次,3.进入后台后的误差范围,4.执行的任务
- (nullable RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block;
//递归block
- (nullable RACDisposable *)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock;

RACScheduler主要是通过不同的子类来实现其功能的,子类需要自己实现:

三个方法。

scheduleRecursiveBlock

其中父类实现了scheduleRecursiveBlock用来实现递归:



- (RACDisposable *)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock {
    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];

    [self scheduleRecursiveBlock:[recursiveBlock copy] addingToDisposable:disposable];
    return disposable;
}

首先,先创建一个销毁任务,并将传入的recursiveBlock拷贝一份然后调用scheduleRecursiveBlock:addingToDisposable:方法,并返回销毁任务。(block没有任何参数返回)。接下来,看调用的这方法:

RACCompoundDisposable *selfDisposable = [RACCompoundDisposable compoundDisposable];
        [disposable addDisposable:selfDisposable];

先创建一个聚合任务,并将它加到刚才创建的销毁任务里。

此block会重复调用scheduleRecursiveBlock方法。

这段代码设计很巧妙,兼顾了异步与同步,先来看下示例代码:

//例一、
[[RACScheduler mainThreadScheduler] scheduleRecursiveBlock:^(void (^ _Nonnull reschedule)(void)) {
        a++;
        NSLog(@"%ld",a);
        
        if (a < 10) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                reschedule();
            });
        }
        
  }];
  
//例二、
   
    __block NSInteger a = 0;
    [[RACScheduler mainThreadScheduler] scheduleRecursiveBlock:^(void (^ _Nonnull reschedule)(void)) {
        a++;
        NSLog(@"%ld",a);
        if (a < 10) {
            reschedule();
        }
        
    }];

这两段代码唯一区别就是reschedule时机,一个延时两秒,一个立刻执行。具体的实现是在调用方法的时候这个reschedule是一个block块,在调用recursiveBlock回调的时候创建

typedef void (^RACSchedulerRecursiveBlock)(void (^reschedule)(void));
void (^reallyReschedule)(void) = ^{
                if (disposable.disposed) return;
                [self scheduleRecursiveBlock:recursiveBlock addingToDisposable:disposable];
            };
__block NSUInteger rescheduleCount = 0;//用来技术,当前未执行block的数量
__block BOOL rescheduleImmediately = NO;//是否立刻执行

@autoreleasepool {
    recursiveBlock(^{
        [lock lock];
        BOOL immediate = rescheduleImmediately;
        if (!immediate) ++rescheduleCount;
        [lock unlock];

        if (immediate) reallyReschedule();
    });
}
[lock lock];
NSUInteger synchronousCount = rescheduleCount;
rescheduleImmediately = YES;
[lock unlock];

for (NSUInteger i = 0; i < synchronousCount; i++) {
    reallyReschedule();
}


这里有个属性,是否立刻执行,默认为NO,如果没有调用或者直接调用这个reschedule,那么这个值为NO,最终会走入:

if (!immediate) ++rescheduleCount;

方法使计数加1,最终在下面的for循环中执行reallyReschedule;若是在异步调用的话,那么rescheduleImmediately会在调用block之前变为YES,引用计数加一也会跳过,如果过段时间调用reschedule那么会走入:

if (immediate) reallyReschedule();

方法调用这个block。这个block会重新调用scheduleRecursiveBlock,从而实现递归。

performAsCurrentScheduler

这个方法是由父类实现,是为了来实现currentScheduler方法,可以很方便的获取到当前的队列

- (void)performAsCurrentScheduler:(void (^)(void))block {
    NSCParameterAssert(block != NULL);
   //获取当前队列
    RACScheduler *previousScheduler = RACScheduler.currentScheduler;
    //设置当前队列为自身NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = self;
  //执行block回调
    @autoreleasepool {
        block();
    }
//block执行完后重新将队列设为之前的,如果之前没有就移除
    if (previousScheduler != nil) {
        NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey] = previousScheduler;
    } else {
        [NSThread.currentThread.threadDictionary removeObjectForKey:RACSchedulerCurrentSchedulerKey];
    }
}

RACImmediateScheduler

RACImmediateScheduler是会直接执行block,立即执行调度的任务,这是唯一一个支持同步执行的调度器,他重写了父类的三个方法:

//直接回调
- (RACDisposable *)schedule:(void (^)(void))block {
    NSCParameterAssert(block != NULL);

    block();
    return nil;
}

//当前线程等待N秒后回调block
- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block {
    NSCParameterAssert(date != nil);
    NSCParameterAssert(block != NULL);

    [NSThread sleepUntilDate:date];
    block();

    return nil;
}
//不支持repeat
- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block {
    NSCAssert(NO, @"+[RACScheduler immediateScheduler] does not support %@.", NSStringFromSelector(_cmd));
    return nil;
}

//重写递归,不支持异步
- (RACDisposable *)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock {
for (__block NSUInteger remaining = 1; remaining > 0; remaining--) {
        recursiveBlock(^{
            remaining++;
        });
    }
}

RACQueueScheduler

异步调度队列,最主要的队列,里面实现了:

- (RACDisposable *)schedule:(void (^)(void))block

实现了父类的CurrentSchedule。

+ (dispatch_time_t)wallTimeWithDate:(NSDate *)date {
    NSCParameterAssert(date != nil);

    double seconds = 0;
    double frac = modf(date.timeIntervalSince1970, &seconds);

    struct timespec walltime = {
        .tv_sec = (time_t)fmin(fmax(seconds, LONG_MIN), LONG_MAX),
        .tv_nsec = (long)fmin(fmax(frac * NSEC_PER_SEC, LONG_MIN), LONG_MAX)
    };

    return dispatch_walltime(&walltime, 0);
}

- (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block {
    NSCParameterAssert(date != nil);
    NSCParameterAssert(block != NULL);

    RACDisposable *disposable = [[RACDisposable alloc] init];

    dispatch_after([self.class wallTimeWithDate:date], self.queue, ^{
        if (disposable.disposed) return;
        [self performAsCurrentScheduler:block];
    });

    return disposable;
}

实现了延时一段时间执行

- (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block{
   NSCParameterAssert(date != nil);
    NSCParameterAssert(interval > 0.0 && interval < INT64_MAX / NSEC_PER_SEC);
    NSCParameterAssert(leeway >= 0.0 && leeway < INT64_MAX / NSEC_PER_SEC);
    NSCParameterAssert(block != NULL);

    uint64_t intervalInNanoSecs = (uint64_t)(interval * NSEC_PER_SEC);
    uint64_t leewayInNanoSecs = (uint64_t)(leeway * NSEC_PER_SEC);

    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue);
    dispatch_source_set_timer(timer, [self.class wallTimeWithDate:date], intervalInNanoSecs, leewayInNanoSecs);
    dispatch_source_set_event_handler(timer, block);
    dispatch_resume(timer);

    return [RACDisposable disposableWithBlock:^{
        dispatch_source_cancel(timer);
    }];
}

使用了GCD定时器,参数分别传,第一次执行的时间,执行时间间隔,精确度,执行的任务

RACSubscriptionScheduler

这个类只是做了一个转化:如果当前有队列,使用当前队列进行任务,如果没有使用默认队列执行任务。

RACTargetQueueScheduler

RACQueueScheduler的子类,设置线程间的优先级,
dispatch_set_target_queue(queue, targetQueue);
使用这个dispatch_set_target_queue方法可以设置队列执行阶层,例如dispatch_set_target_queue(queue, targetQueue);
这样设置时,相当于将queue指派给targetQueue,如果targetQueue是串行队列,则queue是串行执行的;如果targetQueue是并行队列,那么queue是并行的。

dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_set_target_queue(queue1, targetQueue);
    dispatch_set_target_queue(queue2, targetQueue);
    
    dispatch_async(queue1, ^{
        NSLog(@"queue1 1");
    });
    dispatch_async(queue1, ^{
        NSLog(@"queue1 2");
    });
    dispatch_async(queue2, ^{
        NSLog(@"queue2 1");
    });
    dispatch_async(queue2, ^{
        NSLog(@"queue2 2");
    });
    dispatch_async(targetQueue, ^{
        NSLog(@"target queue");
    });


结果


queue1 1
queue1 2
queue2 1
queue2 2
target queue

如果targetQueue为Concurrent Dispatch Queue,那么输出结果可能如下:

queue1 1
queue2 1
queue1 2
target queue
queue2 2

回到RACTargetQueueScheduler中来,在这里传进来的入参是dispatch_get_main_queue( ),这是一个Serial Dispatch Queue,这里再调用dispatch_set_target_queue方法,相当于把queue的优先级设置的和main_queue一致。

注意:同步队列和异步队列的区别

上一篇下一篇

猜你喜欢

热点阅读