YYKit 之 YYDispatchQueuePool 閱讀冕杠,并二次封裝
封裝 queue 的原因
平時(shí)我們使用 GCD 的時(shí)候矛洞,一般這樣
dispatch_async(dispatch_get_global_queue(0, 0), ^{
});
隊(duì)列的申請(qǐng)分配由系統(tǒng)決定洼哎,雖然系統(tǒng)會(huì)幫我們按需創(chuàng)建一些隊(duì)列,但有些時(shí)候沼本,app 大量地方這樣寫噩峦,或者在 for 循環(huán)中,大量創(chuàng)建抽兆,銷毀识补,CPU 是很好資源的并且會(huì)擠占主線程的 cpu 資源,所以 queue 使用不好辫红,也是會(huì)造成卡頓的李请。
YYDispatchQueuePool
原理是通過一個(gè)結(jié)構(gòu)體,結(jié)構(gòu)體里面包含名字厉熟,隊(duì)列組导盅,隊(duì)列個(gè)數(shù)和安全系數(shù),其實(shí)就是一個(gè)自增數(shù)揍瑟,用來去除隊(duì)列白翻,當(dāng)獲取隊(duì)列的時(shí)候,會(huì)從定義好的靜態(tài)數(shù)組中取出對(duì)應(yīng)優(yōu)先級(jí)的隊(duì)列
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;
}
一種優(yōu)先級(jí)對(duì)應(yīng)數(shù)據(jù)一個(gè)位置绢片,一共有五種優(yōu)先級(jí)滤馍,所以數(shù)據(jù)存放五個(gè) context 就可以了,一個(gè)context對(duì)應(yīng)一種優(yōu)先級(jí)的隊(duì)列底循,然后每一種context巢株,會(huì)根據(jù)數(shù)量去創(chuàng)建對(duì)應(yīng)優(yōu)先級(jí)的隊(duì)列,代碼里是根據(jù)版本創(chuàng)建的熙涤,
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);
}
每一種創(chuàng)建一次就可以了阁苞,因?yàn)槭庆o態(tài)變量數(shù)組困檩,所以下次進(jìn)來數(shù)據(jù)就存在了還是上一次的,然后自增安全系數(shù)那槽,從對(duì)應(yīng)的優(yōu)先級(jí)的那個(gè)context的queues里面取出一個(gè) queue 就可以了悼沿,這里有點(diǎn)說不明白了,哈哈骚灸,自己看代碼就好嘍糟趾,很簡(jiǎn)單的,理解思想就好啦甚牲。
這里說一下各個(gè)優(yōu)先級(jí)
NSQualityOfServiceUserInteractive : 該優(yōu)先級(jí)最高义郑,一般和動(dòng)畫UI刷新有關(guān),需要在一瞬間完成
NSQualityOfServiceUserInitiated : 由用戶發(fā)起丈钙,希望立即得到結(jié)果非驮,比如滑動(dòng)scrollview的時(shí)候加載數(shù)據(jù),用于后續(xù)cell的顯示,需要在幾秒甚至更短的時(shí)間內(nèi)完成
NSQualityOfServiceUtility : 需要稍微話費(fèi)點(diǎn)時(shí)間完成的任務(wù),比如下載任務(wù)说榆,在幾秒鐘或幾分鐘完成的任務(wù)
NSQualityOfServiceBackground : 優(yōu)先級(jí)最低渔隶,一般用于后臺(tái)任務(wù),比如數(shù)據(jù)的同步等纵顾,這些任務(wù)不需要馬上返回結(jié)果伍茄,需要幾分鐘或者幾小時(shí)
NSQualityOfServiceDefault : 介于 NSQualityOfServiceUserInitiated 和 NSQualityOfServiceUtility 之間。
基于 YYDispatchQueuePool 的二次封裝
我們平時(shí)肯定都用到這樣的代碼
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
這里的queue是系統(tǒng)分發(fā)的施逾,那么我們能不能基于我們自己的 queue 寫一個(gè)這樣的 API 呢敷矫,答案是可以的,那么思路是什么呢汉额,after 無非即使幾秒鐘之后執(zhí)行曹仗,那么在這幾秒鐘 我們卡住當(dāng)前創(chuàng)建的這個(gè)類的子線程不就OK了嗎?問題迎刃而解蠕搜,那么怎么做呢怎茫?利用信號(hào)量,創(chuàng)建對(duì)象的時(shí)候妓灌,我們把信號(hào)量置為一轨蛤,然后dispatch_semaphore_wait(self.sem, dispatch_time(DISPATCH_TIME_NOW, time * 1000 * 1000 * 1000 ));
這樣不就是可以做到了嗎,API 接口大概是這樣的
/**
獲取一個(gè)默認(rèn)的隊(duì)列虫埂,優(yōu)先級(jí)默認(rèn)為(RCQualityOfServiceDefault)祥山,如果開啟 start 會(huì)在此隊(duì)列中根據(jù)優(yōu)先級(jí)延遲指定 time 時(shí)間后,執(zhí)行 block掉伏。
@param block block
@param time 延遲時(shí)間
@return 隊(duì)列對(duì)象
*/
+ (instancetype)dispatch_Queue_Block_After:(dispatch_block_t)block delay:(NSTimeInterval)time;
/**
獲取一個(gè)指定優(yōu)先級(jí)的隊(duì)列缝呕,如果開啟 start 會(huì)在此隊(duì)列中根據(jù)優(yōu)先級(jí)延遲指定 time 時(shí)間后澳窑,執(zhí)行 block。
@param block block
@param qos 指定優(yōu)先級(jí)
@param time 延遲時(shí)間
@return 隊(duì)列對(duì)象
*/
+ (instancetype)dispatch_Queue_Block_After:(dispatch_block_t)block QOS:(RCQualityOfService)qos delay:(NSTimeInterval)time;
/**
獲取一個(gè)默認(rèn)的隊(duì)列岳颇,優(yōu)先級(jí)默認(rèn)為(RCQualityOfServiceDefault)照捡,如果開啟 start 會(huì)在此隊(duì)列中根據(jù)優(yōu)先級(jí)立刻執(zhí)行 block。
@param block block
@return 隊(duì)列對(duì)象
*/
+ (instancetype)dispatch_Queue_Block:(dispatch_block_t)block;
/**
獲取一個(gè)指定優(yōu)先級(jí)的隊(duì)列话侧,如果開啟 start 會(huì)在此隊(duì)列中根據(jù)優(yōu)先級(jí)立刻執(zhí)行 block栗精。
@param block block
@param qos 優(yōu)先級(jí)
@return 隊(duì)列對(duì)象
*/
+ (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 方法是根據(jù)優(yōu)先級(jí)去創(chuàng)建隊(duì)列,核心代碼是 YY 瞻鹏,然后得到他的 queue 之后悲立,利用信號(hào)量阻塞,這樣就做到了新博,既可以指定優(yōu)先級(jí)薪夕,又可以根據(jù) cpu創(chuàng)建隊(duì)列,而且還可以擁有和系統(tǒng)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];
}
缺點(diǎn)
無法任務(wù)依賴赫悄,比如等一個(gè)任務(wù)做完了之后再做另一個(gè)原献,一個(gè)任務(wù)的延遲需要等待另一個(gè)任務(wù)的延遲執(zhí)行完才可以進(jìn)行,比如 A 延遲一秒執(zhí)行埂淮,2延遲兩秒姑隅,現(xiàn)在的接口是并發(fā)進(jìn)行,總共兩秒可以完成倔撞,要完成B在A執(zhí)行完成之后再執(zhí)行讲仰,目前做不到,待完善痪蝇,如果誰有好的思路歡迎添加修改功能鄙陡,共同進(jìn)步!