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

相关文章

解读GC日志中的各项指标用法

《解读GC日志中的各项指标用法》:本文主要介绍GC日志中的各项指标用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、基础 GC 日志格式(以 G1 为例)1. Minor GC 日志2. Full GC 日志二、关键指标解析1. GC 类型与触发原因2. 堆

MySQL数据库中ENUM的用法是什么详解

《MySQL数据库中ENUM的用法是什么详解》ENUM是一个字符串对象,用于指定一组预定义的值,并可在创建表时使用,下面:本文主要介绍MySQL数据库中ENUM的用法是什么的相关资料,文中通过代码... 目录mysql 中 ENUM 的用法一、ENUM 的定义与语法二、ENUM 的特点三、ENUM 的用法1

JavaSE正则表达式用法总结大全

《JavaSE正则表达式用法总结大全》正则表达式就是由一些特定的字符组成,代表的是一个规则,:本文主要介绍JavaSE正则表达式用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录常用的正则表达式匹配符正则表China编程达式常用的类Pattern类Matcher类PatternSynta

MySQL之InnoDB存储引擎中的索引用法及说明

《MySQL之InnoDB存储引擎中的索引用法及说明》:本文主要介绍MySQL之InnoDB存储引擎中的索引用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录1、背景2、准备3、正篇【1】存储用户记录的数据页【2】存储目录项记录的数据页【3】聚簇索引【4】二

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的

深度解析Python装饰器常见用法与进阶技巧

《深度解析Python装饰器常见用法与进阶技巧》Python装饰器(Decorator)是提升代码可读性与复用性的强大工具,本文将深入解析Python装饰器的原理,常见用法,进阶技巧与最佳实践,希望可... 目录装饰器的基本原理函数装饰器的常见用法带参数的装饰器类装饰器与方法装饰器装饰器的嵌套与组合进阶技巧

Mysql中isnull,ifnull,nullif的用法及语义详解

《Mysql中isnull,ifnull,nullif的用法及语义详解》MySQL中ISNULL判断表达式是否为NULL,IFNULL替换NULL值为指定值,NULLIF在表达式相等时返回NULL,用... 目录mysql中isnull,ifnull,nullif的用法1. ISNULL(expr) → 判

Java中的for循环高级用法

《Java中的for循环高级用法》本文系统解析Java中传统、增强型for循环、StreamAPI及并行流的实现原理与性能差异,并通过大量代码示例展示实际开发中的最佳实践,感兴趣的朋友一起看看吧... 目录前言一、基础篇:传统for循环1.1 标准语法结构1.2 典型应用场景二、进阶篇:增强型for循环2.

Python get()函数用法案例详解

《Pythonget()函数用法案例详解》在Python中,get()是字典(dict)类型的内置方法,用于安全地获取字典中指定键对应的值,它的核心作用是避免因访问不存在的键而引发KeyError错... 目录简介基本语法一、用法二、案例:安全访问未知键三、案例:配置参数默认值简介python是一种高级编

MySQL主从复制与读写分离的用法解读

《MySQL主从复制与读写分离的用法解读》:本文主要介绍MySQL主从复制与读写分离的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、主从复制mysql主从复制原理实验案例二、读写分离实验案例安装并配置mycat 软件设置mycat读写分离验证mycat读