GCD [收藏]

一雕薪、簡(jiǎn)介

iOS所有實(shí)現(xiàn)多線程的方案中辨嗽,GCD應(yīng)該是最有魅力的猜扮,因?yàn)镚CD本身是蘋果公司為多核的并行運(yùn)算提出的解決方案郎笆。GCD在工作時(shí)會(huì)自動(dòng)利用更多的處理器核心谭梗,以充分利用更強(qiáng)大的機(jī)器。GCD是Grand Central Dispatch的簡(jiǎn)稱宛蚓,它是基于C語(yǔ)言的激捏。如果使用GCD,完全由系統(tǒng)管理線程凄吏,我們不需要編寫線程代碼远舅。只需定義想要執(zhí)行的任務(wù),然后添加到適當(dāng)?shù)恼{(diào)度隊(duì)列(dispatch queue)。GCD會(huì)負(fù)責(zé)創(chuàng)建線程和調(diào)度你的任務(wù)痕钢,系統(tǒng)直接提供線程管理

二图柏、調(diào)度隊(duì)列(dispath queue)

  1. GCD的一個(gè)重要概念是隊(duì)列,它的核心理念:將長(zhǎng)期運(yùn)行的任務(wù)拆分成多個(gè)工作單元任连,并將這些單元添加到dispath queue中蚤吹,系統(tǒng)會(huì)為我們管理這些dispath queue,為我們?cè)诙鄠€(gè)線程上執(zhí)行工作單元随抠,我們不需要直接啟動(dòng)和管理后臺(tái)線程裁着。
  1. 系統(tǒng)提供了許多預(yù)定義的dispath queue,包括可以保證始終在主線程上執(zhí)行工作的dispath queue拱她。也可以創(chuàng)建自己的dispath queue二驰,而且可以創(chuàng)建任意多個(gè)。GCD的dispath queue嚴(yán)格遵循FIFO(先進(jìn)先出)原則秉沼,添加到dispath queue的工作單元將始終按照加入dispath queue的順序啟動(dòng)诸蚕。
  2. dispatch queue按先進(jìn)先出的順序,串行或并發(fā)地執(zhí)行任務(wù)
    1> serial dispatch queue一次只能執(zhí)行一個(gè)任務(wù), 當(dāng)前任務(wù)完成才開始出列并啟動(dòng)下一個(gè)任務(wù)
    2> concurrent dispatch queue則盡可能多地啟動(dòng)任務(wù)并發(fā)執(zhí)行

三、創(chuàng)建和管理dispatch queue

  1. 獲得全局并發(fā)Dispatch Queue (concurrent dispatch queue)
    1> 并發(fā)dispatch queue可以同時(shí)并行地執(zhí)行多個(gè)任務(wù),不過并發(fā)queue仍然按先進(jìn)先出的順序來啟動(dòng)任務(wù)氧猬。并發(fā)queue會(huì)在之前的任務(wù)完成之前就出列下一個(gè)任務(wù)并開始執(zhí)行背犯。并發(fā)queue同時(shí)執(zhí)行的任務(wù)數(shù)量會(huì)根據(jù)應(yīng)用和系統(tǒng)動(dòng)態(tài)變化,各種因素包括:可用核數(shù)量、其它進(jìn)程正在執(zhí)行的工作數(shù)量盅抚、其它串行dispatch queue中優(yōu)先任務(wù)的數(shù)量等.
    2> 系統(tǒng)給每個(gè)應(yīng)用提供三個(gè)并發(fā)dispatch queue,整個(gè)應(yīng)用內(nèi)全局共享,三個(gè)queue的區(qū)別是優(yōu)先級(jí)漠魏。你不需要顯式地創(chuàng)建這些queue,使用dispatch_get_global_queue函數(shù)來獲取這三個(gè)queue:
>// 獲取默認(rèn)優(yōu)先級(jí)的全局并發(fā)dispatch queue  
dispatch_queue_t  queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  

第一個(gè)參數(shù)用于指定優(yōu)先級(jí),分別使用DISPATCH_QUEUE_PRIORITY_HIGH和DISPATCH_QUEUE_PRIORITY_LOW兩個(gè)常量來獲取高和低優(yōu)先級(jí)的兩個(gè)queue妄均;第二個(gè)參數(shù)目前未使用到柱锹,默認(rèn)0即可
3> 雖然dispatch queue是引用計(jì)數(shù)的對(duì)象,但你不需要retain和release全局并發(fā)queue。因?yàn)檫@些queue對(duì)應(yīng)用是全局的,retain和release調(diào)用會(huì)被忽略丰包。你也不需要存儲(chǔ)這三個(gè)queue的引用,每次都直接調(diào)用dispatch_get_global_queue獲得queue就行了禁熏。

  1. 創(chuàng)建串行Dispatch Queue (serial dispatch queue)
    1> 應(yīng)用的任務(wù)需要按特定順序執(zhí)行時(shí),就需要使用串行Dispatch Queue,串行queue每次只能執(zhí)行一個(gè)任務(wù)。你可以使用串行queue來替代鎖,保護(hù)共享資源 或可變的數(shù)據(jù)結(jié)構(gòu)邑彪。和鎖不一樣的是,串行queue確保任務(wù)按可預(yù)測(cè)的順序執(zhí)行瞧毙。而且只要你異步地提交任務(wù)到串行queue,就永遠(yuǎn)不會(huì)產(chǎn)生死鎖
    2> 你必須顯式地創(chuàng)建和管理所有你使用的串行queue,應(yīng)用可以創(chuàng)建任意數(shù)量的串行queue,但不要為了同時(shí)執(zhí)行更多任務(wù)而創(chuàng)建更多的串行queue。如果你需要并發(fā)地執(zhí)行大量任務(wù),應(yīng)該把任務(wù)提交到全局并發(fā)queue
    3> 利用dispatch_queue_create函數(shù)創(chuàng)建串行queue,兩個(gè)參數(shù)分別是queue名和一組queue屬性
dispatch_queue_t queue;  
queue = dispatch_queue_create("cn.itcast.queue", NULL);  
  1. 運(yùn)行時(shí)獲得公共QueueGCD提供了函數(shù)讓應(yīng)用訪問幾個(gè)公共dispatch queue:
    1> 使用dispatch_get_current_queue函數(shù)作為調(diào)試用途,或者測(cè)試當(dāng)前queue的標(biāo)識(shí)。在block對(duì)象中調(diào)用這個(gè)函數(shù)會(huì)返回block提交到的queue(這個(gè)時(shí)候queue應(yīng)該正在執(zhí)行中)宙彪。在block對(duì)象之外調(diào)用這個(gè)函數(shù)會(huì)返回應(yīng)用的默認(rèn)并發(fā)queue矩动。2> 使用dispatch_get_main_queue函數(shù)獲得應(yīng)用主線程關(guān)聯(lián)的串行dispatch queue3> 使用dispatch_get_global_queue來獲得共享的并發(fā)queue
  1. Dispatch Queue的內(nèi)存管理
    1> Dispatch Queue和其它dispatch對(duì)象(還有dispatch source)都是引用計(jì)數(shù)的數(shù)據(jù)類型。當(dāng)你創(chuàng)建一個(gè)串行dispatch queue時(shí),初始引用計(jì)數(shù)為 1,你可以使用dispatch_retain和dispatch_release函數(shù)來增加和減少引用計(jì)數(shù)释漆。當(dāng)引用計(jì)數(shù)到達(dá) 0 時(shí),系統(tǒng)會(huì)異步地銷毀這個(gè)queue
    2> 對(duì)dispatch對(duì)象(如dispatch queue)retain和release 是很重要的,確保它們被使用時(shí)能夠保留在內(nèi)存中悲没。和OC對(duì)象一樣,通用的規(guī)則是如果使用一個(gè)傳遞過來的queue,你應(yīng)該在使用前retain,使用完之后release
    3> 你不需要retain或release全局dispatch queue,包括全局并發(fā)dispatch queue和main dispatch queue
    4> 即使你實(shí)現(xiàn)的是自動(dòng)垃圾收集的應(yīng)用,也需要retain和release創(chuàng)建的dispatch queue和其它dispatch對(duì)象。GCD 不支持垃圾收集模型來回收內(nèi)存

四男图、添加任務(wù)到queue

要執(zhí)行一個(gè)任務(wù),你需要將它添加到一個(gè)適當(dāng)?shù)膁ispatch queue,你可以單個(gè)或按組來添加示姿,也可以同步或異步地執(zhí)行一個(gè)任務(wù),也。一旦進(jìn)入到queue,queue會(huì)負(fù)責(zé)盡快地執(zhí)行你的任務(wù)逊笆。一般可以用一個(gè)block來封裝任務(wù)內(nèi)容栈戳。

  1. 添加單個(gè)任務(wù)到queue
    1> 異步添加任務(wù)
    你可以異步或同步地添加一個(gè)任務(wù)到Queue,盡可能地使用dispatch_async或dispatch_async_f函數(shù)異步地調(diào)度任務(wù)。因?yàn)樘砑尤蝿?wù)到Queue中時(shí),無(wú)法確定這些代碼什么時(shí)候能夠執(zhí)行览露。因此異步地添加block或函數(shù),可以讓你立即調(diào)度這些代碼的執(zhí)行,然后調(diào)用線程可以繼續(xù)去做其它事情。特別是應(yīng)用主線程一定要異步地 dispatch 任務(wù),這樣才能及時(shí)地響應(yīng)用戶事件
    2> 同步添加任務(wù)
    少數(shù)時(shí)候你可能希望同步地調(diào)度任務(wù),以避免競(jìng)爭(zhēng)條件或其它同步錯(cuò)誤譬胎。 使用dispatch_sync和dispatch_sync_f函數(shù)同步地添加任務(wù)到Queue,這兩個(gè)函數(shù)會(huì)阻塞當(dāng)前調(diào)用線程,直到相應(yīng)任務(wù)完成執(zhí)行差牛。注意:絕對(duì)不要在任務(wù)中調(diào)用 dispatch_sync或dispatch_sync_f函數(shù),并同步調(diào)度新任務(wù)到當(dāng)前正在執(zhí)行的 queue。對(duì)于串行queue這一點(diǎn)特別重要,因?yàn)檫@樣做肯定會(huì)導(dǎo)致死鎖;而并發(fā)queue也應(yīng)該避免這樣做堰乔。
    3> 代碼演示
// 調(diào)用前偏化,查看下當(dāng)前線程  
NSLog(@"當(dāng)前調(diào)用線程:%@", [NSThread currentThread]);  
  
// 創(chuàng)建一個(gè)串行queue  
dispatch_queue_t queue = dispatch_queue_create("cn.itcast.queue", NULL);  
  
dispatch_async(queue, ^{  
    NSLog(@"開啟了一個(gè)異步任務(wù),當(dāng)前線程:%@", [NSThread currentThread]);  
});  
  
dispatch_sync(queue, ^{  
    NSLog(@"開啟了一個(gè)同步任務(wù)镐侯,當(dāng)前線程:%@", [NSThread currentThread]);  
});  
// 銷毀隊(duì)列  
dispatch_release(queue);

打印信息:

2013-02-03 09:03:37.348 thread[6491:c07] 當(dāng)前調(diào)用線程:<NSThread: 0x714fa80>{name = (null), num = 1}  
2013-02-03 09:03:37.349 thread[6491:1e03] 開啟了一個(gè)異步任務(wù)侦讨,當(dāng)前線程:<NSThread: 0x74520a0>{name = (null), num = 3}  
2013-02-03 09:03:37.350 thread[6491:c07] 開啟了一個(gè)同步任務(wù),當(dāng)前線程:<NSThread: 0x714fa80>{name = (null), num = 1}  

2.并發(fā)地執(zhí)行循環(huán)迭代
如果你使用循環(huán)執(zhí)行固定次數(shù)的迭代, 并發(fā)dispatch queue可能會(huì)提高性能苟翻。
例如下面的for循環(huán):


int i;  
int count = 10;  
for (i = 0; i < count; i++) {  
   printf("%d  ",i);  
}  

1> 如果每次迭代執(zhí)行的任務(wù)與其它迭代獨(dú)立無(wú)關(guān),而且循環(huán)迭代執(zhí)行順序也無(wú)關(guān)緊要的話,你可以調(diào)用dispatch_apply或dispatch_apply_f函數(shù)來替換循環(huán)韵卤。這兩個(gè)函數(shù)為每次循環(huán)迭代將指定的block或函數(shù)提交到queue。當(dāng)dispatch到并發(fā) queue時(shí),就有可能同時(shí)執(zhí)行多個(gè)循環(huán)迭代崇猫。用dispatch_apply或dispatch_apply_f時(shí)你可以指定串行或并發(fā) queue沈条。并發(fā)queue允許同時(shí)執(zhí)行多個(gè)循環(huán)迭代,而串行queue就沒太大必要使用了。
下面代碼使用dispatch_apply替換了for循環(huán),你傳遞的block必須包含一個(gè)size_t類型的參數(shù),用來標(biāo)識(shí)當(dāng)前循環(huán)迭代诅炉。第一次迭代這個(gè)參數(shù)值為0,最后一次值為count - 1

// 獲得全局并發(fā)queue  
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
size_t count = 10;  
dispatch_apply(count, queue, ^(size_t i) {  
    printf("%zd ", i);  
});  
// 銷毀隊(duì)列  
dispatch_release(queue);  

打印信息:

1 2 0 3 4 5 6 7 8 9   

可以看出蜡歹,這些迭代是并發(fā)執(zhí)行的
和普通for循環(huán)一樣,dispatch_apply和dispatch_apply_f函數(shù)也是在所有迭代完成之后才會(huì)返回,因此這兩個(gè)函數(shù)會(huì)阻塞當(dāng)前線程涕烧,主線程中調(diào)用這兩個(gè)函數(shù)必須小心,可能會(huì)阻止事件處理循環(huán)并無(wú)法響應(yīng)用戶事件月而。所以如果循環(huán)代碼需要一定的時(shí)間執(zhí)行,可以考慮在另一個(gè)線程中調(diào)用這兩個(gè)函數(shù)。如果你傳遞的參數(shù)是串行queue,而且正是執(zhí)行當(dāng)前代碼的queue,就會(huì)產(chǎn)生死鎖议纯。

3.在主線程中執(zhí)行任務(wù)
1> GCD提供一個(gè)特殊的dispatch queue,可以在應(yīng)用的主線程中執(zhí)行任務(wù)父款。只要應(yīng)用主線程設(shè)置了run loop(由CFRunLoopRef類型或NSRunLoop對(duì)象管理),就會(huì)自動(dòng)創(chuàng)建這個(gè)queue,并且最后會(huì)自動(dòng)銷毀。非Cocoa應(yīng)用如果不顯式地設(shè)置run loop, 就必須顯式地調(diào)用dispatch_main函數(shù)來顯式地激活這個(gè)dispatch queue,否則雖然你可以添加任務(wù)到queue,但任務(wù)永遠(yuǎn)不會(huì)被執(zhí)行铛漓。
2> 調(diào)用dispatch_get_main_queue函數(shù)獲得應(yīng)用主線程的dispatch queue,添加到這個(gè)queue的任務(wù)由主線程串行化執(zhí)行
3> 代碼實(shí)現(xiàn)溯香,比如異步下載圖片后,回到主線程顯示圖片

// 異步下載圖片  
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
    NSURL *url = [NSURL URLWithString:@"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg"];  
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];  
      
    // 回到主線程顯示圖片  
    dispatch_async(dispatch_get_main_queue(), ^{  
        self.imageView.image = image;  
    });  
});  

4.任務(wù)中使用Objective-C對(duì)象
GCD支持Cocoa內(nèi)存管理機(jī)制,因此可以在提交到queue的block中自由地使用Objective-C對(duì)象浓恶。每個(gè)dispatch queue維護(hù)自己的autorelease pool確保釋放autorelease對(duì)象,但是queue不保證這些對(duì)象實(shí)際釋放的時(shí)間玫坛。如果應(yīng)用消耗大量?jī)?nèi)存,并且創(chuàng)建大量autorelease對(duì)象,你需要?jiǎng)?chuàng)建自己的autorelease pool,用來及時(shí)地釋放不再使用的對(duì)象。

五包晰、暫停和繼續(xù)queue

我們可以使用dispatch_suspend函數(shù)暫停一個(gè)queue以阻止它執(zhí)行block對(duì)象;使用dispatch_resume函數(shù)繼續(xù)dispatch queue湿镀。調(diào)用dispatch_suspend會(huì)增加queue的引用計(jì)數(shù),調(diào)用dispatch_resume則減少queue的引用計(jì)數(shù)。當(dāng)引用計(jì)數(shù)大于0時(shí),queue就保持掛起狀態(tài)伐憾。因此你必須對(duì)應(yīng)地調(diào)用suspend和resume函數(shù)伊群。掛起和繼續(xù)是異步的,而且只在執(zhí)行block之間(比如在執(zhí)行一個(gè)新的block之前或之后)生效。掛起一個(gè)queue不會(huì)導(dǎo)致正在執(zhí)行的block停止潜支。

六娄徊、Dispatch Group的使用

假設(shè)有這樣一個(gè)需求:從網(wǎng)絡(luò)上下載兩張不同的圖片,然后顯示到不同的UIImageView上去胸嘴,一般可以這樣實(shí)現(xiàn)

  // 根據(jù)url獲取UIImage  
- (UIImage *)imageWithURLString:(NSString *)urlString {  
    NSURL *url = [NSURL URLWithString:urlString];  
    NSData *data = [NSData dataWithContentsOfURL:url];  
    return [UIImage imageWithData:data];  
}  
  
- (void)downloadImages {  
    // 異步下載圖片  
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
        // 下載第一張圖片  
        NSString *url1 = @"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg";  
        UIImage *image1 = [self imageWithURLString:url1];  
          
        // 下載第二張圖片  
        NSString *url2 = @"http://hiphotos.baidu.com/lvpics/pic/item/3a86813d1fa41768bba16746.jpg";  
        UIImage *image2 = [self imageWithURLString:url2];  
          
        // 回到主線程顯示圖片  
        dispatch_async(dispatch_get_main_queue(), ^{  
            self.imageView1.image = image1;  
              
            self.imageView2.image = image2;  
        });  
    });  
}  

雖然這種方案可以解決問題雏掠,但其實(shí)兩張圖片的下載過程并不需要按順序執(zhí)行,并發(fā)執(zhí)行它們可以提高執(zhí)行速度劣像。有個(gè)注意點(diǎn)就是必須等兩張圖片都下載完畢后才能回到主線程顯示圖片乡话。Dispatch Group能夠在這種情況下幫我們提升性能。下面先看看Dispatch Group的用處:
我們可以使用dispatch_group_async函數(shù)將多個(gè)任務(wù)關(guān)聯(lián)到一個(gè)Dispatch Group和相應(yīng)的queue中耳奕,group會(huì)并發(fā)地同時(shí)執(zhí)行這些任務(wù)绑青。而且Dispatch Group可以用來阻塞一個(gè)線程, 直到group關(guān)聯(lián)的所有的任務(wù)完成執(zhí)行。有時(shí)候你必須等待任務(wù)完成的結(jié)果,然后才能繼續(xù)后面的處理屋群。
下面用Dispatch Group優(yōu)化上面的代碼:

// 根據(jù)url獲取UIImage  
- (UIImage *)imageWithURLString:(NSString *)urlString {  
    NSURL *url = [NSURL URLWithString:urlString];  
    NSData *data = [NSData dataWithContentsOfURL:url];  
    // 這里并沒有自動(dòng)釋放UIImage對(duì)象  
    return [[UIImage alloc] initWithData:data];  
}  
  
- (void)downloadImages {  
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
      
    // 異步下載圖片  
    dispatch_async(queue, ^{  
        // 創(chuàng)建一個(gè)組  
        dispatch_group_t group = dispatch_group_create();  
          
        __block UIImage *image1 = nil;  
        __block UIImage *image2 = nil;  
          
        // 關(guān)聯(lián)一個(gè)任務(wù)到group  
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
            // 下載第一張圖片  
            NSString *url1 = @"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg";  
            image1 = [self imageWithURLString:url1];  
        });  
          
        // 關(guān)聯(lián)一個(gè)任務(wù)到group  
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
            // 下載第一張圖片  
            NSString *url2 = @"http://hiphotos.baidu.com/lvpics/pic/item/3a86813d1fa41768bba16746.jpg";  
            image2 = [self imageWithURLString:url2];  
        });  
          
        // 等待組中的任務(wù)執(zhí)行完畢,回到主線程執(zhí)行block回調(diào)  
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{  
            self.imageView1.image = image1;  
            self.imageView2.image = image2;  
              
            // 千萬(wàn)不要在異步線程中自動(dòng)釋放UIImage闸婴,因?yàn)楫?dāng)異步線程結(jié)束,異步線程的自動(dòng)釋放池也會(huì)被銷毀芍躏,那么UIImage也會(huì)被銷毀  
              
            // 在這里釋放圖片資源  
            [image1 release];  
            [image2 release];  
        });  
          
        // 釋放group  
        dispatch_release(group);  
    });  
}  

dispatch_group_notify函數(shù)用來指定一個(gè)額外的block掠拳,該block將在group中所有任務(wù)完成后執(zhí)行

GCD例子

//  后臺(tái)執(zhí)行:
 dispatch_async(dispatch_get_global_queue(0, 0), ^{
      // something
 });

 // 主線程執(zhí)行:
 dispatch_async(dispatch_get_main_queue(), ^{
      // something
 });

 // 一次性執(zhí)行:
 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
     // code to be executed once
 });

 // 延遲2秒執(zhí)行:
 double delayInSeconds = 2.0;
 dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
 dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
     // code to be executed on the main queue after delay
 });

 // 自定義dispatch_queue_t
 dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL);
 dispatch_async(urls_queue, ^{  
   // your code 
 });
 dispatch_release(urls_queue);

 // 合并匯總結(jié)果
 dispatch_group_t group = dispatch_group_create();
 dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
      // 并行執(zhí)行的線程一
 });
 dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
      // 并行執(zhí)行的線程二
 });
 dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
      // 匯總結(jié)果
 });

GCD的另一個(gè)用處是可以讓程序在后臺(tái)較長(zhǎng)久的運(yùn)行。

在沒有使用GCD時(shí)纸肉,當(dāng)app被按home鍵退出后溺欧,app僅有最多5秒鐘的時(shí)候做一些保存或清理資源的工作。但是在使用GCD后柏肪,app最多有10分鐘的時(shí)間在后臺(tái)長(zhǎng)久運(yùn)行姐刁。這個(gè)時(shí)間可以用來做清理本地緩存,發(fā)送統(tǒng)計(jì)數(shù)據(jù)等工作烦味。

讓程序在后臺(tái)長(zhǎng)久運(yùn)行的示例代碼如下:

// AppDelegate.h文件
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;

// AppDelegate.m文件
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [self beingBackgroundUpdateTask];
    // 在這里加上你需要長(zhǎng)久運(yùn)行的代碼
    [self endBackgroundUpdateTask];
}

- (void)beingBackgroundUpdateTask
{
    self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [self endBackgroundUpdateTask];
    }];
}

- (void)endBackgroundUpdateTask
{
    [[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
    self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末聂使,一起剝皮案震驚了整個(gè)濱河市壁拉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌柏靶,老刑警劉巖弃理,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異屎蜓,居然都是意外死亡痘昌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門炬转,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辆苔,“玉大人,你說我怎么就攤上這事扼劈∽て。” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵荐吵,是天一觀的道長(zhǎng)骑冗。 經(jīng)常有香客問我,道長(zhǎng)先煎,這世上最難降的妖魔是什么贼涩? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮榨婆,結(jié)果婚禮上磁携,老公的妹妹穿的比我還像新娘褒侧。我一直安慰自己良风,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布闷供。 她就那樣靜靜地躺著烟央,像睡著了一般。 火紅的嫁衣襯著肌膚如雪歪脏。 梳的紋絲不亂的頭發(fā)上疑俭,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天,我揣著相機(jī)與錄音婿失,去河邊找鬼钞艇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛豪硅,可吹牛的內(nèi)容都是我干的哩照。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼懒浮,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼飘弧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤次伶,失蹤者是張志新(化名)和其女友劉穎痴昧,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冠王,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赶撰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了版确。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扣囊。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖绒疗,靈堂內(nèi)的尸體忽然破棺而出侵歇,到底是詐尸還是另有隱情,我是刑警寧澤吓蘑,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布惕虑,位于F島的核電站,受9級(jí)特大地震影響磨镶,放射性物質(zhì)發(fā)生泄漏溃蔫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一琳猫、第九天 我趴在偏房一處隱蔽的房頂上張望伟叛。 院中可真熱鬧,春花似錦脐嫂、人聲如沸统刮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)侥蒙。三九已至,卻和暖如春匀奏,著一層夾襖步出監(jiān)牢的瞬間鞭衩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工娃善, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留论衍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓聚磺,卻偏偏與公主長(zhǎng)得像坯台,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子咧最,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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