GCD 之任務(wù)操作(Dispatch Block)

在向隊(duì)列中添加任務(wù)時(shí)着逐,可以直接在對(duì)應(yīng)的函數(shù)中添加 block呕童。但是如果想對(duì)任務(wù)進(jìn)行操作漆际,比如監(jiān)聽(tīng)任務(wù)、取消任務(wù)夺饲,就需要獲取對(duì)應(yīng)的 block奸汇。

創(chuàng)建block

  • object-c
    創(chuàng)建 block 有兩種方式,第一種方式如下:

    dispatch_block_t dispatch_block_create(dispatch_block_flags_t flags, dispatch_block_t block);
    

    在該函數(shù)中往声,flags 參數(shù)用來(lái)設(shè)置 block 的標(biāo)記擂找,block 參數(shù)用來(lái)設(shè)置具體的任務(wù)。flags 的類型為 dispatch_block_flags_t 的枚舉浩销,用于設(shè)置 block 的標(biāo)記贯涎,定義如下:

    DISPATCH_ENUM(dispatch_block_flags, unsigned long,
        DISPATCH_BLOCK_BARRIER = 0x1,
        DISPATCH_BLOCK_DETACHED = 0x2,
        DISPATCH_BLOCK_ASSIGN_CURRENT = 0x4,
        DISPATCH_BLOCK_NO_QOS_CLASS = 0x8,
        DISPATCH_BLOCK_INHERIT_QOS_CLASS = 0x10,
        DISPATCH_BLOCK_ENFORCE_QOS_CLASS = 0x20,
    );
    

    創(chuàng)建 block 的另一種方式如下:

    dispatch_block_t dispatch_block_create_with_qos_class(dispatch_block_flags_t flags,
        dispatch_qos_class_t qos_class, int relative_priority,
        dispatch_block_t block);
    

    相比于 dispatch_block_create 函數(shù),這種方式在創(chuàng)建 block 的同時(shí)可以指定了相應(yīng)的優(yōu)先級(jí)撼嗓。dispatch_qos_class_tqos_class_t 的別名柬采,定義如下:

    #if __has_include(<sys/qos.h>)
    typedef qos_class_t dispatch_qos_class_t;
    #else
    typedef unsigned int dispatch_qos_class_t;
    #endif
    

    qos_class_t 是一種枚舉,有以下類型:

    • QOS_CLASS_USER_INTERACTIVE:user interactive 等級(jí)表示任務(wù)需要被立即執(zhí)行且警,用來(lái)在響應(yīng)事件之后更新 UI粉捻,來(lái)提供好的用戶體驗(yàn)。這個(gè)等級(jí)最好保持小規(guī)模斑芜。

    • QOS_CLASS_USER_INITIATED:user initiated 等級(jí)表示任務(wù)由 UI 發(fā)起異步執(zhí)行肩刃。適用場(chǎng)景是需要及時(shí)結(jié)果同時(shí)又可以繼續(xù)交互的時(shí)候。

    • QOS_CLASS_DEFAULT:default 默認(rèn)優(yōu)先級(jí)

    • QOS_CLASS_UTILITY:utility 等級(jí)表示需要長(zhǎng)時(shí)間運(yùn)行的任務(wù),伴有用戶可見(jiàn)進(jìn)度指示器盈包。經(jīng)常會(huì)用來(lái)做計(jì)算沸呐,I/O,網(wǎng)絡(luò)呢燥,持續(xù)的數(shù)據(jù)填充等任務(wù)崭添。這個(gè)任務(wù)節(jié)能。

    • QOS_CLASS_BACKGROUND:background 等級(jí)表示用戶不會(huì)察覺(jué)的任務(wù)叛氨,使用它來(lái)處理預(yù)加載呼渣,或者不需要用戶交互和對(duì)時(shí)間不敏感的任務(wù)。

    • QOS_CLASS_UNSPECIFIED:unspecified 未指明

    事例:

    dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"normal do some thing...");
    });
    dispatch_async(concurrentQuene, block);
    
    //
    dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_DEFAULT, 0, ^{
        NSLog(@"qos do some thing...");
    });
    dispatch_async(concurrentQuene, qosBlock);
    
  • swift 3.0
    swift 3.0 中使用了 DispatchWorkItem 對(duì)任務(wù)進(jìn)行了封裝寞埠∑ㄖ茫可以在 DispatchWorkItem 初始化方法中指定任務(wù)的內(nèi)容和優(yōu)先級(jí)。事例如下:

    let concurrentQueue = DispatchQueue.init(label: "concurrentQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil);
    
    let block = DispatchWorkItem.init(block: {
        print("normal do some thing...")
    })
    concurrentQueue.async(execute: block);
    
    let qosBlock = DispatchWorkItem.init(qos: .default, flags: .noQoS, block: {
        print("qos do some thing...")
    })
    

監(jiān)聽(tīng) block 執(zhí)行結(jié)束

有時(shí)我們需要等待特定的 block 執(zhí)行完成之后仁连,再去執(zhí)行其他任務(wù)空执。有兩種方法可以獲取到指定 block 執(zhí)行結(jié)束的時(shí)機(jī)硫豆。

  • object-c
    object-c 中俘种,第一種為使用下面的函數(shù):

    long dispatch_block_wait(dispatch_block_t block, dispatch_time_t timeout);
    

    該函數(shù)會(huì)阻塞當(dāng)前線程進(jìn)行等待茎用。傳入需要設(shè)置的 block 和等待時(shí)間 timeouttimeout 參數(shù)表示函數(shù)在等待 block 執(zhí)行完畢時(shí)伍伤,應(yīng)該等待多久并徘。如果執(zhí)行 block 所需的時(shí)間小于 timeout遣钳,則返回 0扰魂,否則返回非 0 值。此參數(shù)也可以取常量 DISPATCH_TIME_FOREVER蕴茴,這表示函數(shù)會(huì)一直等待 block 執(zhí)行完劝评,而不會(huì)超時(shí)【氲恚可以使用 dispatch_time 函數(shù)和 DISPATCH_TIME_NOW 常量來(lái)方便的設(shè)置具體的超時(shí)時(shí)間蒋畜。
    如果 block 執(zhí)行完成,dispatch_block_wait 就會(huì)立即返回撞叽。不能使用 dispatch_block_wait 來(lái)等待同一個(gè) block 的多次執(zhí)行全部結(jié)束姻成;這種情況可以考慮使用 dispatch_group_wait 來(lái)解決。也不能在多個(gè)線程中愿棋,同時(shí)等待同一個(gè) block 的結(jié)束科展。同一個(gè) block 只能執(zhí)行一次,被等待一次糠雨。

    注意:因?yàn)?dispatch_block_wait 會(huì)阻塞當(dāng)前線程才睹,所以不應(yīng)該放在主線程中調(diào)用。

    事例

    dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(concurrentQuene, ^{
        dispatch_queue_t allTasksQueue = dispatch_queue_create("allTasksQueue", DISPATCH_QUEUE_CONCURRENT);
    
        dispatch_block_t block = dispatch_block_create(0, ^{
            NSLog(@"開(kāi)始執(zhí)行");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"結(jié)束執(zhí)行");
        });
    
        dispatch_async(allTasksQueue, block);
        // 等待時(shí)長(zhǎng),10s 之后超時(shí)
        dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
        long resutl = dispatch_block_wait(block, timeout);
        if (resutl == 0) {
            NSLog(@"執(zhí)行成功");
        } else {
            NSLog(@"執(zhí)行超時(shí)");
        }
    });
    

    輸入結(jié)果:

    開(kāi)始執(zhí)行
    結(jié)束執(zhí)行
    執(zhí)行成功

  • swift 3.0
    在 swift 3.0 中琅攘,只需調(diào)用 DispatchWorkItem 的實(shí)例方法即可等待任務(wù)結(jié)束垮庐,如下:

    public func wait()
    public func wait(timeout: DispatchTime) -> DispatchTimeoutResult
    public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
    

    事例:

    let concurrentQueue = DispatchQueue.init(label: "concurrentQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil);
    
    concurrentQueue.async(execute: {
        let allTesksQueue = DispatchQueue.init(label: "allTesksQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil);
    
        let workItem = DispatchWorkItem.init(block: {
            print("開(kāi)始執(zhí)行")
            sleep(3)
            print("結(jié)束執(zhí)行")
        })
    
        allTesksQueue.async(execute: workItem)
        
        // 設(shè)置超時(shí)時(shí)間,超時(shí)時(shí)間為 10s
        let timeout = DispatchTime.now() + 10
        let result = workItem.wait(timeout: timeout)
    
        if result == .success {
            print("執(zhí)行成功")
        } else {
            print("執(zhí)行超時(shí)")
        }
    })
    

輸入結(jié)果同 object-c坞琴。

第二種監(jiān)聽(tīng) block 執(zhí)行完成的方法如下:

  • object-c
    在 object-c 中可用下面的函數(shù):

    void dispatch_block_notify(dispatch_block_t block,     
            dispatch_queue_t queue, 
            dispatch_block_t notification_block);
    

    該函數(shù)接收三個(gè)參數(shù)哨查,第一個(gè)參數(shù)是需要監(jiān)視的 block,第二個(gè)參數(shù)是監(jiān)聽(tīng)的 block 執(zhí)行結(jié)束之后要提交執(zhí)行的隊(duì)列 queue剧辐,第三個(gè)參數(shù)是待加入到隊(duì)列中的 block解恰。 和 dispatch_block_wait 的不同之處在于:dispatch_block_notify 函數(shù)不會(huì)阻塞當(dāng)前線程。
    例如:

    NSLog(@"---- 開(kāi)始設(shè)置任務(wù) ----");
    dispatch_queue_t serialQueue =   dispatch_queue_create("com.fyf.serialqueue",   DISPATCH_QUEUE_SERIAL);
    
    // 耗時(shí)任務(wù)
    dispatch_block_t taskBlock = dispatch_block_create(0, ^{
        NSLog(@"開(kāi)始耗時(shí)任務(wù)");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"完成耗時(shí)任務(wù)");
    });
    
    dispatch_async(serialQueue, taskBlock);
    
    // 更新 UI
    dispatch_block_t refreshUI = dispatch_block_create(0, ^{
        NSLog(@"更新 UI");
    });
    
    // 設(shè)置監(jiān)聽(tīng)
    dispatch_block_notify(taskBlock, dispatch_get_main_queue(), refreshUI);
    NSLog(@"---- 完成設(shè)置任務(wù) ----");
    

    輸出:

    ---- 開(kāi)始設(shè)置任務(wù) ----
    ---- 完成設(shè)置任務(wù) ----
    開(kāi)始耗時(shí)任務(wù)
    完成耗時(shí)任務(wù)
    更新 UI

  • swift 3.0
    在 swift 3.0 中浙于,可以使用 DispatchWorkItem 的實(shí)例方法進(jìn)行設(shè)置护盈,方法如下:

    public func notify(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, queue: DispatchQueue, execute: @escaping @convention(block) () -> Swift.Void)
    
    public func notify(queue: DispatchQueue, execute: DispatchWorkItem)
    

    事例:

    print("---- 開(kāi)始設(shè)置任務(wù) ----")
    
    let tasksQueue = DispatchQueue.init(label: "com.fyf.tasksQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil);
    
    let refreshUIWorkItem = DispatchWorkItem.init(block: {
        print("更新 UI")
    })
    
    let tasksWorkItem = DispatchWorkItem.init(block: {
        print("開(kāi)始耗時(shí)任務(wù)")
        sleep(3)
        print("完成耗時(shí)任務(wù)")
    })
    
    tasksWorkItem.notify(queue: DispatchQueue.main, execute: refreshUIWorkItem)
    tasksQueue.async(execute: tasksWorkItem)
    
    print("---- 完成設(shè)置任務(wù) ----")
    

    輸出:

    ---- 開(kāi)始設(shè)置任務(wù) ----
    ---- 完成設(shè)置任務(wù) ----
    開(kāi)始耗時(shí)任務(wù)
    完成耗時(shí)任務(wù)
    更新 UI

任務(wù)的取消

iOS8 后 GCD 支持對(duì) dispatch block 的取消。方法如下:

  • object-c
    可以使用下面的函數(shù)取消 dispatch block:

    void dispatch_block_cancel(dispatch_block_t block);
    

    這個(gè)函數(shù)用異步的方式取消指定的 block羞酗。取消操作使將來(lái)執(zhí)行 dispatch block 立即返回腐宋,但是對(duì)已經(jīng)在執(zhí)行的 dispatch block 沒(méi)有任何影響。當(dāng)一個(gè) block 被取消時(shí)檀轨,它會(huì)立即釋放捕獲的資源胸竞。如果要在一個(gè) block 中對(duì)某些對(duì)象進(jìn)行釋放操作,在取消這個(gè) block 的時(shí)候参萄,需要確保內(nèi)存不會(huì)泄漏卫枝。

    例子:

    dispatch_queue_t serialQueue = dispatch_queue_create("com.fyf.serialqueue", DISPATCH_QUEUE_SERIAL);
    
    // 耗時(shí)任務(wù)
    dispatch_block_t firstTaskBlock = dispatch_block_create(0, ^{
        NSLog(@"開(kāi)始第一個(gè)任務(wù)");
        [NSThread sleepForTimeInterval:1.5f];
        NSLog(@"結(jié)束第一個(gè)任務(wù)");
    });
    
    // 耗時(shí)任務(wù)
    dispatch_block_t secTaskBlock = dispatch_block_create(0, ^{
        NSLog(@"開(kāi)始第二個(gè)任務(wù)");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"結(jié)束第二個(gè)任務(wù)");
    });
    
    dispatch_async(serialQueue, firstTaskBlock);
    dispatch_async(serialQueue, secTaskBlock);
    
    // 等待 1s,讓第一個(gè)任務(wù)開(kāi)始運(yùn)行
    [NSThread sleepForTimeInterval:1];
    
    dispatch_block_cancel(firstTaskBlock);
    NSLog(@"嘗試過(guò)取消第一個(gè)任務(wù)");
    
    dispatch_block_cancel(secTaskBlock);
    NSLog(@"嘗試過(guò)取消第二個(gè)任務(wù)");
    

    輸出:

    開(kāi)始第一個(gè)任務(wù)
    嘗試過(guò)取消第一個(gè)任務(wù)
    嘗試過(guò)取消第二個(gè)任務(wù)
    結(jié)束第一個(gè)任務(wù)

    可見(jiàn) dispatch_block_cancel 對(duì)已經(jīng)在執(zhí)行的任務(wù)不起作用讹挎,只能取消尚未執(zhí)行的任務(wù)校赤。

  • swift 3.0
    在 swift 3.0 中,可以使用 DispatchWorkItem 的實(shí)例方法進(jìn)行設(shè)置筒溃,方法如下:

    public func cancel()
    

    事例:

    let tasksQueue = DispatchQueue.init(label: "com.fyf.tasksQueue");
    
    let firstWorkItem = DispatchWorkItem.init(block: {
        print("開(kāi)始第一個(gè)任務(wù)")
        sleep(2)
        print("結(jié)束第一個(gè)任務(wù)")
    })
    
    let secondWorkItem = DispatchWorkItem.init(block: {
        print("開(kāi)始第二個(gè)任務(wù)")
        sleep(2)
        print("結(jié)束第二個(gè)任務(wù)")
    })
    
    tasksQueue.async(execute: firstWorkItem)
    tasksQueue.async(execute: secondWorkItem)
    
    // 等待 1s马篮,讓第一個(gè)任務(wù)開(kāi)始運(yùn)行
    sleep(1)
    
    firstWorkItem.cancel()
    print("嘗試過(guò)取消第一個(gè)任務(wù)")
    secondWorkItem.cancel()
    print("嘗試過(guò)取消第二個(gè)任務(wù)")
    

    輸出內(nèi)容同 object-c

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末怜奖,一起剝皮案震驚了整個(gè)濱河市浑测,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌歪玲,老刑警劉巖迁央,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異滥崩,居然都是意外死亡岖圈,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門夭委,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)幅狮,“玉大人募强,你說(shuō)我怎么就攤上這事〕缟悖” “怎么了擎值?”我有些...
    開(kāi)封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)逐抑。 經(jīng)常有香客問(wèn)我鸠儿,道長(zhǎng),這世上最難降的妖魔是什么厕氨? 我笑而不...
    開(kāi)封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任进每,我火速辦了婚禮,結(jié)果婚禮上命斧,老公的妹妹穿的比我還像新娘田晚。我一直安慰自己,他們只是感情好国葬,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布贤徒。 她就那樣靜靜地躺著,像睡著了一般汇四。 火紅的嫁衣襯著肌膚如雪接奈。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天通孽,我揣著相機(jī)與錄音序宦,去河邊找鬼。 笑死背苦,一個(gè)胖子當(dāng)著我的面吹牛互捌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播糠惫,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼疫剃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了硼讽?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤牲阁,失蹤者是張志新(化名)和其女友劉穎固阁,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體城菊,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡备燃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凌唬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片并齐。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出况褪,到底是詐尸還是另有隱情撕贞,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布测垛,位于F島的核電站捏膨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏食侮。R本人自食惡果不足惜号涯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望锯七。 院中可真熱鬧链快,春花似錦、人聲如沸眉尸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)效五。三九已至地消,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間畏妖,已是汗流浹背脉执。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留戒劫,地道東北人半夷。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像迅细,于是被迫代替她去往敵國(guó)和親巫橄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Managing Units of Work(管理工作單位) 調(diào)度塊允許您直接配置隊(duì)列中各個(gè)工作單元的屬性茵典。它們還...
    edison0428閱讀 7,939評(píng)論 0 1
  • iOS多線程之GCD 什么是GCD GCD(grand central dispatch) 是 libdispat...
    comst閱讀 1,196評(píng)論 0 0
  • NSThread 第一種:通過(guò)NSThread的對(duì)象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 789評(píng)論 0 3
  • GCD(Grand Central Dispatch) 介紹 GCD 屬于系統(tǒng)級(jí)的線程管理湘换,在 Dispatch ...
    fuyoufang閱讀 4,634評(píng)論 0 10
  • 常規(guī)分頁(yè)查詢緩存方案 我們都知道,通過(guò)緩存查詢的結(jié)果统阿,可以極大的提升系統(tǒng)的服務(wù)能力彩倚,以及降低底層服務(wù)或者是數(shù)據(jù)庫(kù)的...
    OneFish閱讀 35,023評(píng)論 6 10