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