美文网首页
RACUnit和RACScheduler

RACUnit和RACScheduler

作者: boy丿log | 来源:发表于2019-04-03 16:11 被阅读0次

    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主要是通过不同的子类来实现其功能的,子类需要自己实现:

      • (RACDisposable *)schedule:(void (^)(void))block;
      • (RACDisposable *)after:(NSDate *)date schedule:(void (^)(void))block
      • (RACDisposable *)after:(NSDate *)date repeatingEvery:(NSTimeInterval)interval withLeeway:(NSTimeInterval)leeway schedule:(void (^)(void))block

    三个方法。

    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一致。

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

    相关文章

      网友评论

          本文标题:RACUnit和RACScheduler

          本文链接:https://www.haomeiwen.com/subject/yruzbqtx.html