源码信息

开源地址: YYDispatchQueuePool

源码解析

按照惯例我们还是先看它的用法:

// 从全局队列中队列池中获取到一个串行队列
dispatch_queue_t queue = YYDispatchQueueGetForQOS(NSQualityOfServiceUtility);

// 获得一个指定类型的串行队列池
YYDispatchQueuePool *pool = [[YYDispatchQueuePool alloc] initWithName:@"file.read" queueCount:5 qos:NSQualityOfServiceBackground];
// 从队列池中获取一个串行队列
dispatch_queue_t queue = [pool queue];

我们接下来就从这两个情景进行分析:

**** 情景一 ****

好了先贴代码:

dispatch_queue_t YYDispatchQueueGetForQOS(NSQualityOfService qos) {
return YYDispatchContextGetQueue(YYDispatchContextGetForQOS(qos));
}

Queality of Service 是 iOS8 提出的一个新的概念,一个高质量的服务就意味着更多的资源得以提供来更快的完成操作, 它可以是如下值:

typedef NS_ENUM(NSInteger, NSQualityOfService) {
NSQualityOfServiceUserInteractive = 0x21,
NSQualityOfServiceUserInitated = 0x19,
NSQualityOfServiceUtility = 0x11,
NSQualityOfServiceBackground = 0x09,
NSQualityOfServiceDefault = -1
} NS_ENUM_AVAILABLE(10_10, 8_0);
  • UserInteractive QoS 用于直接参与提供一个交互式UI,如处理事件或对屏幕的绘制。
  • UserInitiated QoS 用于表示执行工作已经被用户显示提出并且要求结果能够立即展示以便进行进一步的用户交互。
  • Utility QoS 用于表述执行一项工作后,用户并不需要立即得到结果。这一工作通常用户已经请求过或者在初始化的时候已经自动执行,不会阻碍用户用户的进一步交互,通常在用户可见的时间尺度和可能由一个非模态的进度指示器展示给用户。
  • Background QoS 用于那些非用户初始化或可见的工作。通常说来,用户甚至不知道这想工作已经发生,但是它会以最有效的方法运行同时会树丛那些高优先级的QoS。例如:内容抓取,搜索索引,备份,同步与外部系统的数据。
  • Default QoS 表明QoS信息缺失。尽可能的从其它资源推断可能的QoS信息。如果这一推断不成立,一个位于UserInitiated和Utility之间的QoS将得以使用。

这里创建的QoS 为 NSQualityOfServiceUtility:


static YYDispatchContext *YYDispatchContextGetForQOS(NSQualityOfService qos) {
static YYDispatchContext *context[5] = {0};
switch (qos) {
//.........
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;
//.........
}
}

创建的串行队列数和处理器的内核数一致,这里还有一个需要注意的是这里创建了五个YYDispatchContext,也就是每次使用一种QoS都会创建出和内核数相等的当前类型QoS的串行队列。我们看下整个创建过程:

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);
}
return context;
}

这个和YYAsyncDisplay里面显示串行队列的创建代码相一致。

YYDispatchContextGetQueue中会从YYDispatchContext中取出对应的queue,这里用到了OSAtomicIncrement32,每次调用的时候会将context->counter递增1,所以返回的queue会在context->queues中循环顺序取出。

static dispatch_queue_t YYDispatchContextGetQueue(YYDispatchContext *context) {
uint32_t counter = (uint32_t)OSAtomicIncrement32(&context->counter);
void *queue = context->queues[counter % context->queueCount];
return (__bridge dispatch_queue_t)(queue);
}

**** 情景二 ****

有了上面介绍后看下面代码就比较简单了,它通过YYDispatchQueuePool的初始化方法创建一个指定类型Qos的串行队列池,再通过YYDispatchContextGetQueue从前面创建的YYDispatchContext中的queues中去除一个串行队列

- (instancetype)initWithName:(NSString *)name queueCount:(NSUInteger)queueCount qos:(NSQualityOfService)qos {
if (queueCount == 0 || queueCount > MAX_QUEUE_COUNT) return nil;
self = [super init];
_context = YYDispatchContextCreate(name.UTF8String, (uint32_t)queueCount, qos);
if (!_context) return nil;
_name = name;
return self;
}

- (dispatch_queue_t)queue {
return YYDispatchContextGetQueue(_context);
}

下面是根据源码绘制的结构图:

Contents
  1. 1. 源码信息
  2. 2. 源码解析