GCD部分用法

2024-09-02 01:18
文章标签 用法 部分 gcd

本文主要是介绍GCD部分用法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1,用gcd延迟执行任务

如果我们需要某个方法在一段时间后执行,那么我们常常会调用这样的方法

- (void)viewDidLoad{

    [super viewDidLoad];

    [self performSelector:@selector(printString:) withObject:@"Grand Central Dispatch" afterDelay:3.0];

}

- (void) printString:(NSString *)paramString{

    NSLog(@"%@", paramString);

}

但是,我们要讲的是gcd方式,那么在gcd的世界里,也有2个类似的方法来实现延迟执行的功能

dispatch_after 方法


void dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);


when:所指定的时间


queue:指定的队列


block:执行的Block


Block加入指定队列,并按照指定时间执行。

先上例子:

- (void)viewDidLoad{

    [super viewDidLoad];

    

    double delayInSeconds = 2.0;

 

    // 创建延期的时间 2S,因为dispatch_time使用的时间是纳秒,尼玛,比毫秒还小,太夸张了!!!

    dispatch_time_t delayInNanoSeconds =dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    // 得到全局队列

    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 延期执行

    dispatch_after(delayInNanoSeconds, concurrentQueue, ^(void){

        NSLog(@"Output GCD !");

        

    });

}

dispatch_time 方法


dispatch_time_t dispatch_time(dispatch_time_t when,int64_t delta);


when:指定的开始点,可以用 DISPATCH_TIME_NOW 来指定一个当前的时间点


delta:纳秒数


创建一个时间点(dispatch_time_t),返回的时间点为:delta+ when


dispatch_time_t:纯粹就是一个时间点。


dispatch_after 方法


void dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);


when:时间点


queue:指定的队列


block:执行的Block


Block加入到指定的队列,并且在指定的时间点执行。



dispatch_after_f 方法


我想有了,前面知识的铺垫,这个方法理解起来,就像在切菜!!!


直接上例子:


- (void)viewDidLoad{

    [super viewDidLoad];

    

    double delayInSeconds = 2.0;

    dispatch_time_t delayInNanoSeconds =dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    

    dispatch_after_f(delayInNanoSeconds,concurrentQueue, @"GCD", processSomething);

    

}


void processSomething(void *paramContext){

    

    NSLog(@"This is %@",paramContext);

}


注意:dispatch_after_f 执行的是一个纯C函数(processSomething)!



2,用gcd来实现单例模式

APP 的生命周期内你想确保每段代码只执行一次,即使它在代码的不同地方多次调用(比如单例的初始化)


直接代码:


static dispatch_once_t onceToken;

void (^executedOnlyOnce)(void) = ^{

    static NSUInteger numberOfEntries = 0;

    numberOfEntries++;

    NSLog(@"Executed %lu time(s)", (unsigned long)numberOfEntries);

};


调用:

dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);


dispatch_once(&onceToken, ^{

    dispatch_async(concurrentQueue,executedOnlyOnce);

});


dispatch_once(&onceToken, ^{

    dispatch_async(concurrentQueue,executedOnlyOnce);

});


可以发现,输出只有一行,虽然我们调用了2次。因为我们传入的dispatch_once_t是相同的。编译器只执行一次操作。


dispatch_once 方法


void dispatch_once(dispatch_once_t *predicate,dispatch_block_t block);


predicate:单例标识符

block:执行的Block


Application周期内,只执行一次Block。即:单例模式


dispatch_once_t:可以理解为单例标识符,dispatch_once方法用它来测试Block是否被执行过了。如果执行过了,那么就不在执行。



3,用gcd将任务分组

有时候,我们可能执行一系列的任务。由于彼此之间的依赖关系。比如有3个任务:ABC;我们必须执行了任务A,才能执行任务B,最后执行任务C。这样的话,我们可以用GCD的分组机制来将多个任务来按照预定的顺序来执行。


先看例子吧:


我们先来搞清楚以下的关于调度组的一些常用方法


dispatch_group_t 方法


typedef struct dispatch_group_s *dispatch_group_t;


指代一个调度组。

调度组负责监视加入到该组的多个Block。每个Block可以是异步或者同步(取决与你自己)。

调度组中的Block可以在不同的队列中执行;并且一个Block可以被添加到多个调度组中。


dispatch_group_create 方法


dispatch_group_t dispatch_group_create(void);


创建一个调度组


dispatch_group_async 方法


void dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);


group:要分发到的调度组


queue:执行Block的队列


block:要执行的Block


Block分发到指定的队列(用指定的队列来执行Block),并且将该Block加入到指定的调度组中。


dispatch_group_notify 方法


void dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);


当调度组中的所有Block被执行完成后,将调用被分配到指定队列的Block


理论一大堆了,还是用事实来说话:


例子:


- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    dispatch_group_t taskGroup = dispatch_group_create();// 创建一个调度组

    dispatch_queue_t mainQueue = dispatch_get_main_queue();// 创建队列

    

    // 任务1

    // Block添加到指定的调度组(taskGroup)中,并且该Block用指定的队列(mainQueue)执行。

    dispatch_group_async(taskGroup, mainQueue, ^{

        

        [self reloadTableView];

    });

    

    // 任务2

    // Block添加到指定的调度组(taskGroup)中,并且该Block用指定的队列(mainQueue)执行。

    dispatch_group_async(taskGroup, mainQueue, ^{

        

        [self reloadScrollView];

    });

    

    // 任务3

    // Block添加到指定的调度组(taskGroup)中,并且该Block用指定的队列(mainQueue)执行。

    dispatch_group_async(taskGroup, mainQueue, ^{

        

        [self reloadImageView];

    });

    // 当指定调度组(taskGroup)中的所有Block都执行完成后,将执行给定的Block,用指定的队列(mainQueue)。

    dispatch_group_notify(taskGroup, mainQueue, ^{

        // 指定的Block

        [[[UIAlertView alloc] initWithTitle:@"Finished" message:@"All tasks are finished" delegate:nil

                          cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show];

        

    });

    

    // 最后,必须release 掉调度组(taskGroup

    dispatch_release(taskGroup);

    

}


#pragma mark - 执行的多个方法


- (void) reloadTableView{

    

    NSLog(@"%s", __FUNCTION__);

}


- (void) reloadScrollView{

    

    NSLog(@"%s", __FUNCTION__);

}


- (void) reloadImageView{

    

    NSLog(@"%s", __FUNCTION__);

    

}


可见,执行顺序,跟加入到调度组中的次序是一样的。


等等,有点疑惑是不是?!

我对每个任务用的是方法dispatch_group_async,是异步的啊。为什么顺序却不变呢?!

呵呵,基础不扎实啊!因为他们都是分配给同一个队列dispatch_get_main_queue() 中的!在一个队列中是串行的啊。所以,还是按照顺序来。


对,在GCD中,每个方法往往都有两种,一种是执行标准Block,一种是执行C函数的。


那么,就ok,我们来看看执行C函数的写法:


例子:


- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    dispatch_group_t taskGroup = dispatch_group_create();

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    dispatch_group_async_f(taskGroup, mainQueue,(void *)self, reloadAllComponents);

    

    dispatch_group_notify(taskGroup, mainQueue, ^{

        

        [[[UIAlertView alloc] initWithTitle:@"Finished"

                                    message:@"All Tasks are Finished"

                                   delegate:nil

                          cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show];

    });

    

    dispatch_release(taskGroup);

    

}


// 定义调度组要执行的C函数

void reloadAllComponents(void *context){

    MoreViewController *self =(MoreViewController *)context;

    [self reloadTableView];

    [self reloadScrollView];

    [self reloadImageView];

}

注意:

因为dispatch_group_async_f不像dispatch_group_t那样使用Block,可以访问当前类的变量。


由于 dispatch_group_async_f 接受 C 函数作为一个代码块来执行,所以,我们要执行reloadTableView方法,reloadScrollView方法,reloadImageView方法,必须有个当前类的引用!!!!!

那么 C 函数必须有一 个引用到 Self,这个 Self 能够调用当前对象的实例方法


4,用gcd创建自己的分发队列

当然,GCD也给予了我们很多自主性质的操作。就是可以定义我们自己的分发队列。

有时候,突发奇想,自己定义一个队列,做自己的事情!那该多自由!这一定可以有的,哟,亲!!

爽啊!!!

注意:我们自定义的队列,往往不会在主队列中执行。而是单独分配一个线程来维护我们所定义的队列。


先来看代码吧:


- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    // 创建指定的自定义的串行队列

    dispatch_queue_t firstSerialQueue = dispatch_queue_create("com.pixolity.GCD.serialQueue1", NULL);

    

    // 让队列异步执行Block

    dispatch_async(firstSerialQueue, ^{

        NSUInteger counter = 0;

        for (counter = 0; counter < 5; counter++){

            NSLog(@"First iteration, counter = %lu", (unsigned long)counter); }

        NSLog(@"Current thread = %@", [NSThread currentThread]);

    });

    

    dispatch_async(firstSerialQueue, ^{

        NSUInteger counter = 0; for (counter = 0;counter < 5;counter++){

            NSLog(@"Second iteration, counter = %lu", (unsigned long)counter);

            NSLog(@"Current thread = %@", [NSThread currentThread]);

        }

    });

    

    dispatch_async(firstSerialQueue, ^{ NSUInteger counter = 0;

        for (counter = 0;counter < 5;counter++){

            NSLog(@"Third iteration, counter = %lu", (unsigned long)counter);

            NSLog(@"Current thread = %@", [NSThread currentThread]);

        }

    });

    // 销毁队列

    dispatch_release(firstSerialQueue);

    

    // 输出主队列,比较会发现,我们自定义的队列,并不在主线程上,效率还是蛮高的。

    dispatch_queue_t mainQueue1 = dispatch_get_main_queue();

    dispatch_async(mainQueue1, ^(void) {

        NSLog(@"Main thread = %@", [NSThread mainThread]);

    });

}


上面是Block形式的。还有一个C函数的自定义


// 定义任务1-C函数形式

void firstIteration(void *paramContext){

    NSUInteger counter = 0;

    for (counter = 0;counter < 5;counter++){

        NSLog(@"First iteration, counter = %lu", (unsigned long)counter);

    }

}


// 定义任务2-C函数形式

void secondIteration(void *paramContext){

    NSUInteger counter = 0;

    for (counter = 0;counter < 5;counter++){

        NSLog(@"Second iteration, counter = %lu", (unsigned long)counter);

    }

}


// 定义任务3-C函数形式

void thirdIteration(void *paramContext){

    NSUInteger counter = 0;

    for (counter = 0;counter < 5;counter++){

        NSLog(@"Third iteration, counter = %lu", (unsigned long)counter);

    }

}


- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    dispatch_queue_t firstSerialQueue = dispatch_queue_create("com.pixolity.GCD.serialQueue1", 0);

    dispatch_async_f(firstSerialQueue, NULL, firstIteration);

    dispatch_async_f(firstSerialQueue, NULL, secondIteration);

    dispatch_async_f(firstSerialQueue, NULL, thirdIteration);

    dispatch_release(firstSerialQueue);

}



这篇关于GCD部分用法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1128586

相关文章

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

Python itertools中accumulate函数用法及使用运用详细讲解

《Pythonitertools中accumulate函数用法及使用运用详细讲解》:本文主要介绍Python的itertools库中的accumulate函数,该函数可以计算累积和或通过指定函数... 目录1.1前言:1.2定义:1.3衍生用法:1.3Leetcode的实际运用:总结 1.1前言:本文将详

MyBatis-Flex BaseMapper的接口基本用法小结

《MyBatis-FlexBaseMapper的接口基本用法小结》本文主要介绍了MyBatis-FlexBaseMapper的接口基本用法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具... 目录MyBATis-Flex简单介绍特性基础方法INSERT① insert② insertSelec

深入解析Spring TransactionTemplate 高级用法(示例代码)

《深入解析SpringTransactionTemplate高级用法(示例代码)》TransactionTemplate是Spring框架中一个强大的工具,它允许开发者以编程方式控制事务,通过... 目录1. TransactionTemplate 的核心概念2. 核心接口和类3. TransactionT

数据库使用之union、union all、各种join的用法区别解析

《数据库使用之union、unionall、各种join的用法区别解析》:本文主要介绍SQL中的Union和UnionAll的区别,包括去重与否以及使用时的注意事项,还详细解释了Join关键字,... 目录一、Union 和Union All1、区别:2、注意点:3、具体举例二、Join关键字的区别&php

oracle中exists和not exists用法举例详解

《oracle中exists和notexists用法举例详解》:本文主要介绍oracle中exists和notexists用法的相关资料,EXISTS用于检测子查询是否返回任何行,而NOTE... 目录基本概念:举例语法pub_name总结 exists (sql 返回结果集为真)not exists (s

Springboot中Jackson用法详解

《Springboot中Jackson用法详解》Springboot自带默认json解析Jackson,可以在不引入其他json解析包情况下,解析json字段,下面我们就来聊聊Springboot中J... 目录前言Jackson用法将对象解析为json字符串将json解析为对象将json文件转换为json

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl

bytes.split的用法和注意事项

当然,我很乐意详细介绍 bytes.Split 的用法和注意事项。这个函数是 Go 标准库中 bytes 包的一个重要组成部分,用于分割字节切片。 基本用法 bytes.Split 的函数签名如下: func Split(s, sep []byte) [][]byte s 是要分割的字节切片sep 是用作分隔符的字节切片返回值是一个二维字节切片,包含分割后的结果 基本使用示例: pa

UVM:callback机制的意义和用法

1. 作用         Callback机制在UVM验证平台,最大用处就是为了提高验证平台的可重用性。在不创建复杂的OOP层次结构前提下,针对组件中的某些行为,在其之前后之后,内置一些函数,增加或者修改UVM组件的操作,增加新的功能,从而实现一个环境多个用例。此外还可以通过Callback机制构建异常的测试用例。 2. 使用步骤         (1)在UVM组件中内嵌callback函