美文网首页IOS学习揽收
GCD之dispatch_once 方法

GCD之dispatch_once 方法

作者: 指尖弹灰 | 来源:发表于2016-10-26 06:24 被阅读278次


    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

    相关文章

      网友评论

        本文标题:GCD之dispatch_once 方法

        本文链接:https://www.haomeiwen.com/subject/zedduttx.html