YYKit 之 YYDispatchQueuePool 阅读,并二次封装(附demo)

YYKit 之 YYDispatchQueuePool 阅读,并二次封装

封装 queue 的原因

平时我们使用 GCD 的时候,一般这样


dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
    });
    

队列的申请分配由系统决定,虽然系统会帮我们按需创建一些队列,但有些时候,app 大量地方这样写,或者在 for 循环中,大量创建,销毁,CPU 是很好资源的并且会挤占主线程的 cpu 资源,所以 queue 使用不好,也是会造成卡顿的。

YYDispatchQueuePool

原理是通过一个结构体,结构体里面包含名字,队列组,队列个数和安全系数,其实就是一个自增数,用来去除队列,当获取队列的时候,会从定义好的静态数组中取出对应优先级的队列

static YYDispatchContext *context[5] = {0};
    switch (qos) {
        case NSQualityOfServiceUserInteractive: {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
                context[0] = YYDispatchContextCreate("com.ibireme.yykit.user-interactive", count, qos);
            });
            return context[0];
        } break;
        case NSQualityOfServiceUserInitiated: {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
                context[1] = YYDispatchContextCreate("com.ibireme.yykit.user-initiated", count, qos);
            });
            return context[1];
        } break;
        case NSQualityOfServiceUtility: {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
                context[2] = YYDispatchContextCreate("com.ibireme.yykit.utility", count, qos);
            });
            return context[2];
        } break;
        case NSQualityOfServiceBackground: {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
                context[3] = YYDispatchContextCreate("com.ibireme.yykit.background", count, qos);
            });
            return context[3];
        } break;
        case NSQualityOfServiceDefault:
        default: {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
                context[4] = YYDispatchContextCreate("com.ibireme.yykit.default", count, qos);
            });
            return context[4];
        } break;
    }

一种优先级对应数据一个位置,一共有五种优先级,所以数据存放五个 context 就可以了,一个context对应一种优先级的队列,然后每一种context,会根据数量去创建对应优先级的队列,代码里是根据版本创建的,

static YYDispatchContext *YYDispatchContextCreate(const char *name,
                                                 uint32_t queueCount,
                                                 NSQualityOfService qos) {
    YYDispatchContext *context = calloc(1, sizeof(YYDispatchContext));
    if (!context) return NULL;
    context->queues =  calloc(queueCount, sizeof(void *));
    if (!context->queues) {
        free(context);
        return NULL;
    }
    if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
        dispatch_qos_class_t qosClass = NSQualityOfServiceToQOSClass(qos);
        for (NSUInteger i = 0; i < queueCount; i++) {
            dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, qosClass, 0);
            dispatch_queue_t queue = dispatch_queue_create(name, attr);
            context->queues[i] = (__bridge_retained void *)(queue);
        }
    } else {
        long identifier = NSQualityOfServiceToDispatchPriority(qos);
        for (NSUInteger i = 0; i < queueCount; i++) {
            dispatch_queue_t queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL);
            dispatch_set_target_queue(queue, dispatch_get_global_queue(identifier, 0));
            context->queues[i] = (__bridge_retained void *)(queue);
        }
    }
    context->queueCount = queueCount;
    if (name) {
         context->name = strdup(name);
    }
  

每一种创建一次就可以了,因为是静态变量数组,所以下次进来数据就存在了还是上一次的,然后自增安全系数,从对应的优先级的那个context的queues里面取出一个 queue 就可以了,这里有点说不明白了,哈哈,自己看代码就好喽,很简单的,理解思想就好啦。

这里说一下各个优先级


NSQualityOfServiceUserInteractive : 该优先级最高,一般和动画UI刷新有关,需要在一瞬间完成

NSQualityOfServiceUserInitiated : 由用户发起,希望立即得到结果,比如滑动scrollview的时候加载数据,用于后续cell的显示,需要在几秒甚至更短的时间内完成
NSQualityOfServiceUtility : 需要稍微话费点时间完成的任务,比如下载任务,在几秒钟或几分钟完成的任务
NSQualityOfServiceBackground : 优先级最低,一般用于后台任务,比如数据的同步等,这些任务不需要马上返回结果,需要几分钟或者几小时
NSQualityOfServiceDefault : 介于 NSQualityOfServiceUserInitiated 和  NSQualityOfServiceUtility 之间。


基于 YYDispatchQueuePool 的二次封装

我们平时肯定都用到这样的代码


dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
    });
    

这里的queue是系统分发的,那么我们能不能基于我们自己的 queue 写一个这样的 API 呢,答案是可以的,那么思路是什么呢,after 无非即使几秒钟之后执行,那么在这几秒钟 我们卡住当前创建的这个类的子线程不就OK了吗?问题迎刃而解,那么怎么做呢?利用信号量,创建对象的时候,我们把信号量置为一,然后dispatch_semaphore_wait(self.sem, dispatch_time(DISPATCH_TIME_NOW, time * 1000 * 1000 * 1000 ));这样不就是可以做到了吗,API 接口大概是这样的


/**
 获取一个默认的队列,优先级默认为(RCQualityOfServiceDefault),如果开启 start 会在此队列中根据优先级延迟指定 time 时间后,执行 block。
 
 @param block block
 @param time 延迟时间
 @return 队列对象
 */
+ (instancetype)dispatch_Queue_Block_After:(dispatch_block_t)block delay:(NSTimeInterval)time;
/**
 获取一个指定优先级的队列,如果开启 start 会在此队列中根据优先级延迟指定 time 时间后,执行 block。
 
 @param block block
 @param qos 指定优先级
 @param time 延迟时间
 @return 队列对象
 */
+ (instancetype)dispatch_Queue_Block_After:(dispatch_block_t)block QOS:(RCQualityOfService)qos delay:(NSTimeInterval)time;

/**
 获取一个默认的队列,优先级默认为(RCQualityOfServiceDefault),如果开启 start 会在此队列中根据优先级立刻执行 block。
 
 @param block block
 @return 队列对象
 */
+ (instancetype)dispatch_Queue_Block:(dispatch_block_t)block;

/**
 获取一个指定优先级的队列,如果开启 start 会在此队列中根据优先级立刻执行 block。
 
 @param block block
 @param qos 优先级
 @return 队列对象
 */
+ (instancetype)dispatch_Queue_Block:(dispatch_block_t)block QOS:(RCQualityOfService)qos;

核心代码大概是这样的

- (instancetype)initQueueWithQOS:(RCQualityOfService)qos block:(dispatch_block_t)block time:(NSTimeInterval)time{
    if (self = [super init]) {
        self.delay = time;
        self.block = block;
        NSQualityOfService nsqos = [self switchQos:qos];
        self.currentQOS = nsqos;
        self.queuePointer = RCDispatchQueuePoolWithBlock;
        self.sem = dispatch_semaphore_create(0);
        [self start];
    }
    return self;
}
- (void)start{
    self.queuePointer(self.currentQOS, ^(dispatch_queue_t queue) {
        dispatch_async(queue, ^{
            NSLog(@"%@",queue);
           [self dispatch_Queue_after1:queue block:self.block delay:self.delay];
        });
    });
}

- (void)dispatch_Queue_after1:(dispatch_queue_t)queue block:(dispatch_block_t)block  delay:(NSTimeInterval) time{
    dispatch_semaphore_wait(self.sem, dispatch_time(DISPATCH_TIME_NOW, time * 1000 * 1000 * 1000 ));
    block();
    dispatch_semaphore_signal(self.sem);
}

start 方法是根据优先级去创建队列,核心代码是 YY ,然后得到他的 queue 之后,利用信号量阻塞,这样就做到了,既可以指定优先级,又可以根据 cpu创建队列,而且还可以拥有和系统api一样效果的接口

使用方法

dispatch_queue_t queue = [RCDispatchQueue dispatchQueue:(RCQualityOfServiceUserInteractive)];
    dispatch_async(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
    NSLog(@"hello");
    [RCDispatchQueue dispatch_Queue_Block_After:^{
        NSLog(@"111");
    } delay:1];
    [RCDispatchQueue dispatch_Queue_Block_After:^{
        NSLog(@"222");
    } delay:2];

    [RCDispatchQueue dispatch_Queue_Block_After:^{
        NSLog(@"333");
    } QOS:RCQualityOfServiceUserInteractive delay:3];

    for (NSInteger i = 0 ; i < 100; i ++) {
        [RCDispatchQueue dispatch_Queue_Block:^{
            
        } QOS:RCQualityOfServiceBackground];
    }

缺点

无法任务依赖,比如等一个任务做完了之后再做另一个,一个任务的延迟需要等待另一个任务的延迟执行完才可以进行,比如 A 延迟一秒执行,2延迟两秒,现在的接口是并发进行,总共两秒可以完成,要完成B在A执行完成之后再执行,目前做不到,待完善,如果谁有好的思路欢迎添加修改功能,共同进步!

DEMO

你可能感兴趣的:(YYKit 之 YYDispatchQueuePool 阅读,并二次封装(附demo))