YYKit 之 YYDispatchQueuePool 閱讀,并二次封裝(附demo)

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)步!

DEMO

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末躏啰,一起剝皮案震驚了整個(gè)濱河市趁矾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌给僵,老刑警劉巖愈魏,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異想际,居然都是意外死亡培漏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門胡本,熙熙樓的掌柜王于貴愁眉苦臉地迎上來牌柄,“玉大人,你說我怎么就攤上這事侧甫∩河叮” “怎么了蹋宦?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長咒锻。 經(jīng)常有香客問我冷冗,道長,這世上最難降的妖魔是什么惑艇? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任蒿辙,我火速辦了婚禮,結(jié)果婚禮上滨巴,老公的妹妹穿的比我還像新娘思灌。我一直安慰自己,他們只是感情好恭取,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布泰偿。 她就那樣靜靜地躺著,像睡著了一般蜈垮。 火紅的嫁衣襯著肌膚如雪耗跛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天攒发,我揣著相機(jī)與錄音调塌,去河邊找鬼。 笑死晨继,一個(gè)胖子當(dāng)著我的面吹牛烟阐,可吹牛的內(nèi)容都是我干的搬俊。 我是一名探鬼主播紊扬,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼唉擂!你這毒婦竟也來了餐屎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤玩祟,失蹤者是張志新(化名)和其女友劉穎腹缩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體空扎,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡藏鹊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了转锈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盘寡。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖撮慨,靈堂內(nèi)的尸體忽然破棺而出竿痰,到底是詐尸還是另有隱情脆粥,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布影涉,位于F島的核電站变隔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蟹倾。R本人自食惡果不足惜匣缘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望喊式。 院中可真熱鬧孵户,春花似錦、人聲如沸岔留。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽献联。三九已至竖配,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間里逆,已是汗流浹背进胯。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留原押,地道東北人胁镐。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像诸衔,于是被迫代替她去往敵國和親盯漂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359