GCD之dispatch_once 方法

96
指尖弹灰
2016.10.26 06:24* 字数 861


dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);

该函数的作用是保证block在程序的生命周期范围内只执行一次。

参数

predicate:该参数是为了表示该block是否执行过。 0表示未执行过  ,1表示执行过         block:该block就是只执行一次的任务。

通过下面的例1和例2,我们可以知道每次执行block前先对 onceToken 的值进行检查,0表示第一次执行,执行block后将onceToken 置为-1.如果onceToken 为-1,表示非第一执行,则不执行block.

例1:

// dispatch_once_t 必须是全局变量或static变量(保证dispatch_once_t只有一份实例)

static dispatch_once_t onceToken;

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

dispatch_once(&onceToken, ^{

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

});

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

运行结果:

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);

运行结果:

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


应用:如果想要某个行为在整个生命周期中只能被执行一次。最常用到的地方是例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(@"进入了哈哈哈");     

              if (!share) {         

                   share = [[TestObject alloc]init];       

              }

        });       

  return share;

}

@end

调用:

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

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

}

结果:

2016-10-26 06:12:47.859 Test1[12436:886784] 进入了哈哈哈

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>

可以发现多次“创建”对象的内存是一样的。也只有第一次创建时真正的创建。


原理:

dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);

进入该方法,我们可以去看一下它的实现

void  _dispatch_once(dispatch_once_t *predicate, dispatch_block_t block)  {

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

                    dispatch_once(predicate, block);  //a

          }

}

如果predicate != -1  执行if里面的语句。因为没法看到a语句具体的实现,我们只能知道 a语句会执行block,同时将predicate = -1。但具体如何实现不知道。

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

__builtin_expect是GCC(version>=2.9)引进的宏,其作用就是帮助编译器判断条件跳转的预期值,避免跳转造成时间浪费.__builtin_expect仅仅是告诉编译器优化,并没有改变其对真值的判断。

比如在内核代码中,经常会遇到  #define likely(x)  __builtin_expect(!!(x), 1)

__builtin_expect()告诉编译器,x可能是1的概率很大,编译器可以假设x是1的情况去对接下来的操作进行优化,以减少指令跳转带来的性能降低。但具体是不是1还要进行正式的判断,毕竟你x不一定是1.

理解1.2  ~0l

~0l 表示一个长整形(long)的0按位取反。  0l  写成16进制  0x00000000  ~0l  就是 0xFFFFFFFF  即-1

IOS初级