GCD之dispatch_once 方法


dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);

該函數(shù)的作用是保證block在程序的生命周期范圍內(nèi)只執(zhí)行一次。

參數(shù)

predicate:該參數(shù)是為了表示該block是否執(zhí)行過。 0表示未執(zhí)行過? ,1表示執(zhí)行過 ? ? ? ? block:該block就是只執(zhí)行一次的任務(wù)。

通過下面的例1和例2,我們可以知道每次執(zhí)行block前先對 onceToken 的值進(jìn)行檢查,0表示第一次執(zhí)行,執(zhí)行block后將onceToken 置為-1.如果onceToken 為-1,表示非第一執(zhí)行,則不執(zhí)行block.

例1:

// dispatch_once_t 必須是全局變量或static變量(保證dispatch_once_t只有一份實(shí)例)

static dispatch_once_t onceToken;

NSLog(@"AonceToken = %ld", onceToken);

dispatch_once(&onceToken, ^{

? ? ? ?NSLog(@"BonceToken = %ld", onceToken);

});

NSLog(@"ConceToken = %ld", onceToken);

運(yùn)行結(jié)果:

2016-10-26 05:55:21.727 Test1[12244:878037] AonceToken = 0

2016-10-26 05:55:21.727 Test1[12244:878037] BonceToken = 140734777330000

2016-10-26 05:55:21.727 Test1[12244:878037] ConceToken = -1

例2:

static dispatch_once_t onceToken;

NSLog(@"AonceToken = %ld", onceToken);

dispatch_once(&onceToken, ^{

? ? ? ?NSLog(@"BonceToken = %ld", onceToken);

});

dispatch_once(&onceToken, ^{

? ? ?NSLog(@"DonceToken = %ld", onceToken);

});

NSLog(@"ConceToken = %ld", onceToken);

運(yùn)行結(jié)果:

2016-10-26 05:59:54.945 Test1[12277:880650] AonceToken = 0

2016-10-26 05:59:54.945 Test1[12277:880650] BonceToken = 140734583380288

2016-10-26 05:59:54.945 Test1[12277:880650] ConceToken = -1


應(yīng)用:如果想要某個(gè)行為在整個(gè)生命周期中只能被執(zhí)行一次。最常用到的地方是例3 單例

例3:?單例

#import <Foundation/Foundation.h>

@interface TestObject : NSObject

+ (TestObject *)shareObject;

@end

@implementation TestObject

+ (TestObject *)shareObject {? ?

? ? ? ?static TestObject *share = nil;? ? ? ?

? ? ? ?static dispatch_once_t onceToken;?

? ? ? ?dispatch_once(&onceToken, ^{? ?

? ? ? ? ? ? ? NSLog(@"進(jìn)入了哈哈哈");? ? ?

? ? ? ? ? ? ? if (!share) {? ? ? ? ?

? ? ? ? ? ? ? ? ? ?share = [[TestObject alloc]init]; ? ? ??

? ? ? ? ? ? ? }

? ? ? ? }); ? ? ??

? return share;

}

@end

調(diào)用:

for (int i=0; i<2; i++) {

NSLog(@"第%d次--%@",i,[TestObject shareObject]);

}

結(jié)果:

2016-10-26 06:12:47.859 Test1[12436:886784] 進(jìn)入了哈哈哈

2016-10-26 06:12:47.860 Test1[12436:886784] 第0次--<TestObject:0x7f8263d4b860>

2016-10-26 06:12:47.861 Test1[12436:886784] 第1次--<TestObject:0x7f8263d4b860>

可以發(fā)現(xiàn)多次“創(chuàng)建”對象的內(nèi)存是一樣的。也只有第一次創(chuàng)建時(shí)真正的創(chuàng)建。


原理:

dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);

進(jìn)入該方法,我們可以去看一下它的實(shí)現(xiàn)

void ?_dispatch_once(dispatch_once_t *predicate, dispatch_block_t block) ?{

? ? ? ? ? if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {

? ? ? ? ? ? ? ? ? ? dispatch_once(predicate, block);? //a

? ? ? ? ? }

}

如果predicate != -1? 執(zhí)行if里面的語句。因?yàn)闆]法看到a語句具體的實(shí)現(xiàn),我們只能知道 a語句會執(zhí)行block,同時(shí)將predicate = -1。但具體如何實(shí)現(xiàn)不知道。

理解1.1:#define DISPATCH_EXPECT(x, v) __builtin_expect((x), (v))

__builtin_expect是GCC(version>=2.9)引進(jìn)的宏,其作用就是幫助編譯器判斷條件跳轉(zhuǎn)的預(yù)期值,避免跳轉(zhuǎn)造成時(shí)間浪費(fèi).__builtin_expect僅僅是告訴編譯器優(yōu)化,并沒有改變其對真值的判斷。

比如在內(nèi)核代碼中,經(jīng)常會遇到? #define likely(x)? __builtin_expect(!!(x), 1)

__builtin_expect()告訴編譯器,x可能是1的概率很大,編譯器可以假設(shè)x是1的情況去對接下來的操作進(jìn)行優(yōu)化,以減少指令跳轉(zhuǎn)帶來的性能降低。但具體是不是1還要進(jìn)行正式的判斷,畢竟你x不一定是1.

理解1.2? ~0l

~0l 表示一個(gè)長整形(long)的0按位取反。? 0l? 寫成16進(jìn)制? 0x00000000? ~0l? 就是 0xFFFFFFFF? 即-1

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 目錄 dispatch_once dispatch_once低負(fù)載特性 備注 參考文章 相信大家對dispatch...
    時(shí)間在改變閱讀 1,670評論 0 6
  • GCD是異步執(zhí)行任務(wù)的技術(shù)支之一,開發(fā)者只需要將想要執(zhí)行的block任務(wù)添加到適當(dāng)?shù)腄ispatch Queue(...
    zziazm閱讀 1,773評論 0 8
  • 在開發(fā)中使用單例是最經(jīng)常不過的事兒了,最常用的就是dispatch_once這個(gè)函數(shù),這個(gè)函數(shù)可以使其參數(shù)內(nèi)的bl...
    ilovehusky閱讀 13,247評論 8 52
  • 我們知道在iOS開發(fā)中,一共有四種多線程技術(shù):pthread,NSThread,GCD,NSOperation: ...
    請叫我周小帥閱讀 1,507評論 0 1
  • iOS中GCD的使用小結(jié) 作者dullgrass 2015.11.20 09:41*字?jǐn)?shù) 4996閱讀 20199...
    DanDanC閱讀 881評論 0 0