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

相关文章

JDK21对虚拟线程的几种用法实践指南

《JDK21对虚拟线程的几种用法实践指南》虚拟线程是Java中的一种轻量级线程,由JVM管理,特别适合于I/O密集型任务,:本文主要介绍JDK21对虚拟线程的几种用法,文中通过代码介绍的非常详细,... 目录一、参考官方文档二、什么是虚拟线程三、几种用法1、Thread.ofVirtual().start(

Java8 Collectors.toMap() 的两种用法

《Java8Collectors.toMap()的两种用法》Collectors.toMap():JDK8中提供,用于将Stream流转换为Map,本文给大家介绍Java8Collector... 目录一、简单介绍用法1:根据某一属性,对对象的实例或属性做映射用法2:根据某一属性,对对象集合进行去重二、Du

Python中isinstance()函数原理解释及详细用法示例

《Python中isinstance()函数原理解释及详细用法示例》isinstance()是Python内置的一个非常有用的函数,用于检查一个对象是否属于指定的类型或类型元组中的某一个类型,它是Py... 目录python中isinstance()函数原理解释及详细用法指南一、isinstance()函数

Python中的sort方法、sorted函数与lambda表达式及用法详解

《Python中的sort方法、sorted函数与lambda表达式及用法详解》文章对比了Python中list.sort()与sorted()函数的区别,指出sort()原地排序返回None,sor... 目录1. sort()方法1.1 sort()方法1.2 基本语法和参数A. reverse参数B.

vue监听属性watch的用法及使用场景详解

《vue监听属性watch的用法及使用场景详解》watch是vue中常用的监听器,它主要用于侦听数据的变化,在数据发生变化的时候执行一些操作,:本文主要介绍vue监听属性watch的用法及使用场景... 目录1. 监听属性 watch2. 常规用法3. 监听对象和route变化4. 使用场景附Watch 的

Java Instrumentation从概念到基本用法详解

《JavaInstrumentation从概念到基本用法详解》JavaInstrumentation是java.lang.instrument包提供的API,允许开发者在类被JVM加载时对其进行修改... 目录一、什么是 Java Instrumentation主要用途二、核心概念1. Java Agent

Java 中 Optional 的用法及最佳实践

《Java中Optional的用法及最佳实践》在Java开发中,空指针异常(NullPointerException)是开发者最常遇到的问题之一,本篇文章将详细讲解Optional的用法、常用方... 目录前言1. 什么是 Optional?主要特性:2. Optional 的基本用法2.1 创建 Opti

Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧

《Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧》本文将通过实际代码示例,深入讲解Python函数的基本用法、返回值特性、全局变量修改以及异常处理技巧,感兴趣的朋友跟随小编一起看看... 目录一、python函数定义与调用1.1 基本函数定义1.2 函数调用二、函数返回值详解2.1 有返

Python中logging模块用法示例总结

《Python中logging模块用法示例总结》在Python中logging模块是一个强大的日志记录工具,它允许用户将程序运行期间产生的日志信息输出到控制台或者写入到文件中,:本文主要介绍Pyt... 目录前言一. 基本使用1. 五种日志等级2.  设置报告等级3. 自定义格式4. C语言风格的格式化方法

SpringBoot 获取请求参数的常用注解及用法

《SpringBoot获取请求参数的常用注解及用法》SpringBoot通过@RequestParam、@PathVariable等注解支持从HTTP请求中获取参数,涵盖查询、路径、请求体、头、C... 目录SpringBoot 提供了多种注解来方便地从 HTTP 请求中获取参数以下是主要的注解及其用法:1