GCD单次执行与多次执行(disatch_once与dispatch_apply)

上一讲将到了GCD的信号量. GCD信号量(dispatch_semaphore)

本篇我们来谈论一下dispath_once.

不知道大家是否用过单例.记不记得单例的调用一般是怎么调用的.如果是创建一个单例.对应的创建方法是否是应该执行一次.

单例每次"创建"的时候改变的值在后面再次"创建"的时候的值是一样的.这是因为单例的创建于一般对象的创建有一些区别.

拿苹果的单例举例子吧.一般都是类方法.要么是shared.要么是default.对比一下一些UIK控件的类方法.比如UIAlertController.每次UIAlertController创建出来的都是一个新的.和之前改动的不一样的对象.而NSNotificationCenter或者是其他的一些类的单例类方法拿出来的似乎都是一个全局的对象.

那是因为单例的创建方法只走一次.通过dispatch_once,我们可以轻松的写下一个单例的创建方法

typedef long dispatch_once_t;
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
void dispatch_once_f(dispatch_once_t *predicate, void *context, dispatch_function_t function);

类型

首先,dispatch_once_t是一个类型,原始类型是long

dispatch_once中需要传入的predicate是指向dispatch_once_t结构的指针,该结构用于测试块是否已完成。

block中的代码只会执行一次

注意:首先,这个dispatch_once是线程安全的.多个线程同时调用该函数的话,该函数会同步仍待,直到块完成.并且.predicate参数是需要指向存储在全局或者静态返回中的变量.

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
	NSLog(@"1");
});

如果不用static修饰dispatch_once_t onceToken也是只执行一次…因为第二次调用直接会崩溃(报EXC_BAD_INSTRUCTION错误)

单例的定义

+ (instancetype)sharedClass {
    static dispatch_once_t once_Token;
    static ClassXXX *instanceXXX;
    dispatch_once(&once_Token, ^{
        instanceXXX = [[ClassXXX alloc] init];
    });
    return instanceXXX;
}

调用sharedClass来创建对象的话会是一个全局的单例.

dispatch_once_fdispatch_once相比.仅仅只是多了一个参数void *context.

dispatch_once_f使用用例:


void function(void *params) {
	NSLog(@"%@", (__bridge id)params);
}

- (IBAction)buttonClick:(NSButton *)sender {
	static dispatch_once_t onceToken;
	dispatch_function_t func = &function;
	/// 不带参数
//	  dispatch_once_f(&onceToken, nil, func);
	/// 带参数
	dispatch_once_f(&onceToken, ((__bridge_retained void*) @[@"1", @"2"]), func);
}

重点,onceToken作为dispatch_once的标识,只要是这个标识的,执行的blokc只会执行一次.

拓展

既然有执行一次.那么就有执行多次的方法.

`void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));`
`void dispatch_apply_f(size_t iterations, dispatch_queue_t queue, void *context, void (*work)(void *, size_t));`

测试代码

- (IBAction)buttonClick:(NSButton *)sender {
    NSLog(@"begin");
    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
	[NSThread sleepForTimeInterval:2];
	NSLog(@"index:%zu ,%@",index ,[NSThread currentThread]);
	NSLog(@"end");
}

输出结果

begin
index:0 ,<NSThread: 0x600001703580>{number = 1, name = main}
index:3 ,<NSThread: 0x60000173fb00>{number = 8, name = (null)}
index:2 ,<NSThread: 0x60000177cec0>{number = 5, name = (null)}
index:1 ,<NSThread: 0x600001773a40>{number = 7, name = (null)}
index:7 ,<NSThread: 0x60000177cec0>{number = 5, name = (null)}
index:5 ,<NSThread: 0x60000173fb00>{number = 8, name = (null)}
index:4 ,<NSThread: 0x600001703580>{number = 1, name = main}
index:6 ,<NSThread: 0x600001773a40>{number = 7, name = (null)}
index:8 ,<NSThread: 0x60000177cec0>{number = 5, name = (null)}
index:9 ,<NSThread: 0x60000173fb00>{number = 8, name = (null)}
end

我们可以看到使用dispatch_apply的时候主线程会阻塞.虽说让他在全局队列上执行.却不一定是新开线程.有些像信号量或者是调度组.的用法

那个size_t的回调类型是当前执行的任务数(执行到第几个了).

带参数

void work(void *params ,size_t size) {
    NSLog(@"size:%ld params:%@ thread:%@", size ,params ,[NSThread currentThread]);
}

- (IBAction)buttonClick:(NSButton *)sender {
    NSLog(@"begin");
    NSArray *array = @[@"1" ,@"2"];
    dispatch_apply_f(10, dispatch_get_global_queue(0, 0), (__bridge_retained void*)array, work);
    NSLog(@"end");
}

打印结果

2019-05-21 17:10:46.932015+0800 GCD[19623:364378] begin
2019-05-21 17:10:46.932443+0800 GCD[19623:365415] size:3 params:(
    1,
    2
) thread:<NSThread: 0x60000177d540>{number = 4, name = (null)}
2019-05-21 17:10:46.932443+0800 GCD[19623:366958] size:1 params:(
    1,
    2
) thread:<NSThread: 0x6000017802c0>{number = 2, name = (null)}
2019-05-21 17:10:46.932444+0800 GCD[19623:364378] size:0 params:(
    1,
    2
) thread:<NSThread: 0x600001703ec0>{number = 1, name = main}
2019-05-21 17:10:46.932444+0800 GCD[19623:364742] size:2 params:(
    1,
    2
) thread:<NSThread: 0x600001749d40>{number = 3, name = (null)}
2019-05-21 17:10:46.932595+0800 GCD[19623:364742] size:7 params:(
    1,
    2
) thread:<NSThread: 0x600001749d40>{number = 3, name = (null)}
2019-05-21 17:10:46.932595+0800 GCD[19623:366958] size:4 params:(
    1,
    2
) thread:<NSThread: 0x6000017802c0>{number = 2, name = (null)}
2019-05-21 17:10:46.932595+0800 GCD[19623:365415] size:5 params:(
    1,
    2
) thread:<NSThread: 0x60000177d540>{number = 4, name = (null)}
2019-05-21 17:10:46.932595+0800 GCD[19623:364378] size:6 params:(
    1,
    2
) thread:<NSThread: 0x600001703ec0>{number = 1, name = main}
2019-05-21 17:10:46.932661+0800 GCD[19623:366958] size:8 params:(
    1,
    2
) thread:<NSThread: 0x6000017802c0>{number = 2, name = (null)}
2019-05-21 17:10:46.932662+0800 GCD[19623:364742] size:9 params:(
    1,
    2
) thread:<NSThread: 0x600001749d40>{number = 3, name = (null)}
2019-05-21 17:10:46.933153+0800 GCD[19623:364378] end

可以看出来,_f就是多了一个可以带参数的能力,其余还是一样的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值