GCD基本用法大全

GCD是Apple开发的一个多核编程的解决方案,他可以用于多核的并行运算,并自动利用更多的CPU内核,同时GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程),而且我们使用GCD时,只需要调用GCD的API来填入需要执行的任务,而不需要编写任何线程管理的代码

一、任务和队列

任务:

执行的操作,任务分为同步执行和异步执行两种。

  • 同步(sync):同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行,即会阻塞线程。只能在当前线程中执行任务(是当前线程,不一定是主线程),不具备开启新线程的能力。API为dispatch_sync这类函数

  • 异步(async):线程会立即返回,无需等待就会继续执行下面的任务,不阻塞当前线程。可以在新的线程中执行任务,具备开启新线程的能力(并不一定开启新线程)。如果不是添加到主队列上,异步会在子线程中执行任务。API为dispatch_async这类函数

队列

队列:存放任务的队列,队列是一种特殊的线性表,遵从FIFO(First in first out,先进先出)原则,分为两种:串行队列和并行队列

  • 并行队列(concurrent):多个任务同时执行(每个任务在不同的线程中执行),API为dispatch_async这类函数

  • 串行队列(serial):任务按照顺序执行(各个任务可能在同一个线程,也可能在不同的线程),API为dispatch_sync这类函数

二、GCD四种基础操作

1.串行队列 + 同步执行

- (void)dispatchSyncSerial{
    //这个函数创建队列,第一个参数为队列名,第二个参数标记创建的队列是串行队列还是并行队列,使用两个宏定义来创建,DISPATCH_QUEUE_SERIAL为串行队列,DISPATCH_QUEUE_CONCURRENT为并行队列
    dispatch_queue_t queue = dispatch_queue_create("串行队列名", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        sleep(1);
        NSLog(@"在串行队列中执行第一个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"在串行队列中执行第二个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        sleep(2);
        NSLog(@"在串行队列中执行第三个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"在串行队列中执行第四个任务,线程:%@",[NSThread currentThread]);
    });
}

/* 结果

2017-08-22 18:42:55.397 GCD简介与Demo[4463:266445] 在串行队列中执行第一个任务,线程:{number = 1, name = main}

2017-08-22 18:42:55.398 GCD简介与Demo[4463:266445] 在串行队列中执行第二个任务,线程:{number = 1, name = main}

2017-08-22 18:42:57.399 GCD简介与Demo[4463:266445] 在串行队列中执行第三个任务,线程:{number = 1, name = main}

2017-08-22 18:42:57.400 GCD简介与Demo[4463:266445] 在串行队列中执行第四个任务,线程:{number = 1, name = main}

*/

结论:串行队列+同步执行,任务按照添加的顺序,依次执行,并且都在同一个线程中执行

2.串行队列 + 异步执行

- (void)dispatchAsyncSerial{
    dispatch_queue_t queue = dispatch_queue_create("串行队列名", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"在串行队列中执行第一个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"在串行队列中执行第二个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"在串行队列中执行第三个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"在串行队列中执行第四个任务,线程:%@",[NSThread currentThread]);
    });
}
/* 结果 
2017-08-22 18:43:45.006 GCD简介与Demo[4481:267419] 在串行队列中执行第一个任务,线程:{number = 3, name = (null)}

2017-08-22 18:43:45.006 GCD简介与Demo[4481:267419] 在串行队列中执行第二个任务,线程:{number = 3, name = (null)}

2017-08-22 18:43:47.011 GCD简介与Demo[4481:267419] 在串行队列中执行第三个任务,线程:{number = 3, name = (null)}

2017-08-22 18:43:47.011 GCD简介与Demo[4481:267419] 在串行队列中执行第四个任务,线程:{number = 3, name = (null)}

*/

结论:串行队列 + 异步执行,虽然各个任务都在在子线程中执行,但仍然是按照添加入队列的顺序,依次执行

3.并行队列 + 同步执行

- (void)dispatchSyncConcurrent{
    dispatch_queue_t queue = dispatch_queue_create("并行队列名", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue, ^{
        sleep(1);
        NSLog(@"在并行队列中执行第一个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"在并行队列中执行第二个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        sleep(2);
        NSLog(@"在并行队列中执行第三个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"在并行队列中执行第四个任务,线程:%@",[NSThread currentThread]);
    });
}
/*
 结果
 2017-08-22 18:48:30.946 GCD简介与Demo[4535:272013] 在并行队列中执行第一个任务,线程:{number = 1, name = main}
 2017-08-22 18:48:30.947 GCD简介与Demo[4535:272013] 在并行队列中执行第二个任务,线程:{number = 1, name = main}
 2017-08-22 18:48:32.948 GCD简介与Demo[4535:272013] 在并行队列中执行第三个任务,线程:{number = 1, name = main}
 2017-08-22 18:48:32.949 GCD简介与Demo[4535:272013] 在并行队列中执行第四个任务,线程:{number = 1, name = main}
 */

结论:并行队列 + 同步执行,虽然队里是并行队列,但是由于是同步执行,所有任务都在主线程中执行,他们仍然是按照添加入队列的顺序,依次执行

4.并行队列 + 异步执行

- (void)dispatchAsyncConcurrent{
    dispatch_queue_t queue = dispatch_queue_create("并行队列名", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"在并行队列中执行第一个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"在并行队列中执行第二个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"在并行队列中执行第三个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"在并行队列中执行第四个任务,线程:%@",[NSThread currentThread]);
    });
}
/*
 结果
 2017-08-22 18:51:02.246 GCD简介与Demo[4574:274917] 在并行队列中执行第二个任务,线程:{number = 3, name = (null)}
 2017-08-22 18:51:02.246 GCD简介与Demo[4574:274937] 在并行队列中执行第四个任务,线程:{number = 4, name = (null)}
 2017-08-22 18:51:03.249 GCD简介与Demo[4574:274918] 在并行队列中执行第一个任务,线程:{number = 5, name = (null)}
 2017-08-22 18:51:04.250 GCD简介与Demo[4574:274920] 在并行队列中执行第三个任务,线程:{number = 6, name = (null)}
 */

结论:并行队列 + 异步执行,各个任务都在子线程中执行,并且同时开始执行

5.GCD中的特殊队列

  1. 主队列:GCD自带的一个特殊的串行队列,使用dispatch_get_main_queue()获取

  2. 全局队列:GCD自带的一个特殊的并行队列,使用dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)获取

注意:DISPATCH_QUEUE_PRIORITY_DEFAULT是一个宏,它标记队列的优先级,有以下四种:

  • DISPATCH_QUEUE_PRIORITY_HIGH 高优先级

  • DISPATCH_QUEUE_PRIORITY_DEFAULT 默认优先级

  • DISPATCH_QUEUE_PRIORITY_LOW 低优先级

  • DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台优先级

三、线程间的通信以及单次执行方法

1.线程间的通信

一般用于在将网络请求、下载、上传等耗时操作放入子线程执行完毕后,回到主线程更新UI

注意:UI操作必须在主线程进行操作

- (void)gcdSendMessageFromAnotherThread{
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"开始执行耗时操作,所处线程:%@",[NSThread currentThread]);
        sleep(3);
        NSLog(@"耗时操作执行完毕,开始进入主线程");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"进入主线程,开始执行更新UI操作,所处线程:%@",[NSThread currentThread]);
        });
        
    });
}
/*
 结果
 2017-08-22 19:07:09.099 GCD简介与Demo[4698:290444] 开始执行耗时操作,所处线程:{number = 3, name = (null)}
 2017-08-22 19:07:12.103 GCD简介与Demo[4698:290444] 耗时操作执行完毕,开始进入主线程
 2017-08-22 19:07:12.103 GCD简介与Demo[4698:290380] 进入主线程,开始执行更新UI操作,所处线程:{number = 1, name = main}
 */

2.单次执行 dispatch_once 一般用于创建单例

- (void)dispatchOnce{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"这里是只执行一次的代码,所处线程:%@",[NSThread currentThread]);
    });
}
- (void)testDispatchOnce{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"第一次,所处线程:%@",[NSThread currentThread]);
        [self dispatchOnce];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"第二次,所处线程:%@",[NSThread currentThread]);
        [self dispatchOnce];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"第三次,所处线程:%@",[NSThread currentThread]);
        [self dispatchOnce];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"第四次,所处线程:%@",[NSThread currentThread]);
        [self dispatchOnce];
    });
}
/* 
 结果
 2017-08-22 19:12:15.813 GCD简介与Demo[4729:295603] 第三次,所处线程:{number = 5, name = (null)}
 2017-08-22 19:12:15.813 GCD简介与Demo[4729:295617] 第四次,所处线程:{number = 6, name = (null)}
 2017-08-22 19:12:15.813 GCD简介与Demo[4729:295600] 第二次,所处线程:{number = 4, name = (null)}
 2017-08-22 19:12:15.813 GCD简介与Demo[4729:295601] 第一次,所处线程:{number = 3, name = (null)}
 2017-08-22 19:12:15.814 GCD简介与Demo[4729:295603] 这里是只执行一次的代码,所处线程:{number = 5, name = (null)}
 */

结论:dispatch_once封装的代码只在第一次执行了一次

四、GCD的其它处理函数

1.栅栏方法dispatch_barrier

这个函数相当于一个停顿,在这个函数之前加入队列的任务先执行,执行完毕后执行栅栏函数中的任务,然后再执行栅栏函数之后的任务

注意:dispatch_barrier_async和dispatch_barrier_sync的区别:仅仅是执行的线程不同,dispatch_barrier_sync会同步执行(在主线程中执行),dispatch_barrier_async在子线程中执行

- (void)dispatchBarrier{
    dispatch_queue_t queue = dispatch_queue_create("并行队列名", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"第一个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"第二个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_barrier_sync(queue, ^{
        sleep(1);
        NSLog(@"执行barrier同步任务,线程:%@",[NSThread currentThread]);
        sleep(1);
    });
    dispatch_barrier_async(queue, ^{
        sleep(1);
        NSLog(@"执行barrier异步任务,线程:%@",[NSThread currentThread]);
        sleep(1);
    });
    dispatch_async(queue, ^{
        NSLog(@"第三个任务,线程:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"第四个任务,线程:%@",[NSThread currentThread]);
    });
}
/*
 结果:
 2017-08-22 19:35:16.874 GCD简介与Demo[4949:314086] 第一个任务,线程:{number = 3, name = (null)}
 2017-08-22 19:35:16.874 GCD简介与Demo[4949:314083] 第二个任务,线程:{number = 4, name = (null)}
 2017-08-22 19:35:17.876 GCD简介与Demo[4949:313797] 执行barrier同步任务,线程:{number = 1, name = main}
 2017-08-22 19:35:19.882 GCD简介与Demo[4949:314083] 执行barrier异步任务,线程:{number = 4, name = (null)}
 2017-08-22 19:35:20.887 GCD简介与Demo[4949:314083] 第三个任务,线程:{number = 4, name = (null)}
 2017-08-22 19:35:20.887 GCD简介与Demo[4949:314084] 第四个任务,线程:{number = 5, name = (null)}
 */

2.GCD延迟执行的方法dispatch_after

- (void)dispatchAfter{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"执行子线程任务,线程:%@",[NSThread currentThread]);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"延时两秒后执行主线程任务,线程:%@",[NSThread currentThread]);
        });
    });
    
}
/*
 结果
 2017-08-22 19:40:10.880 GCD简介与Demo[4982:319700] 执行子线程任务,线程:{number = 3, name = (null)}
 2017-08-22 19:40:13.072 GCD简介与Demo[4982:319625] 延时两秒后执行主线程任务,线程:{number = 1, name = main}
 */

3.GCD的快速迭代方法dispatch_apply

dispatch_apply类似for循环,它是一个同步调用,任务将会执行给定次数,如果是并行队里中,那么会并发执行任务,如果是串行队列中,则会顺序执行其中的代码

应用场景:

  1. 防止创建过多线程导致线程爆炸

  2. 多个任务并行执行,比如字典数组的快速解析

- (void)dispatchApplyAsync{
    NSArray *array = @[@1,@2,@3,@4,@5];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       dispatch_apply(array.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
           NSLog(@"%@开始执行 %zu times",[NSThread currentThread],index+1);
           sleep((unsigned int)(index+1));
       });
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"回到主线程操作");
        });
    });
}
/*
 结果
 2017-08-22 19:54:38.939 GCD简介与Demo[5088:331292] {number = 5, name = (null)}开始执行 3 times
 2017-08-22 19:54:38.939 GCD简介与Demo[5088:331304] {number = 4, name = (null)}开始执行 2 times
 2017-08-22 19:54:38.939 GCD简介与Demo[5088:331290] {number = 3, name = (null)}开始执行 1 times
 2017-08-22 19:54:38.939 GCD简介与Demo[5088:331289] {number = 6, name = (null)}开始执行 4 times
 2017-08-22 19:54:38.940 GCD简介与Demo[5088:331290] {number = 3, name = (null)}开始执行 5 times
 2017-08-22 19:54:42.943 GCD简介与Demo[5088:331197] 回到主线程操作
 */

可以看出,dispatch_apply之后的操作将会在dispatch_apply全部执行完毕后才开始执行

- (void)dispatchApplySync{
    NSArray *array = @[@1,@2,@3,@4,@5];
    dispatch_queue_t queue = dispatch_queue_create("串行队列名", DISPATCH_QUEUE_SERIAL);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_apply(array.count, queue, ^(size_t index) {
            NSLog(@"%@开始执行 %zu times",[NSThread currentThread],index+1);
            sleep((unsigned int)(index+1));
        });
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"回到主线程操作");
        });
    });
}
/*
 结果
 2017-08-22 19:58:03.952 GCD简介与Demo[5110:334355] {number = 3, name = (null)}开始执行 1 times
 2017-08-22 19:58:04.954 GCD简介与Demo[5110:334355] {number = 3, name = (null)}开始执行 2 times
 2017-08-22 19:58:06.958 GCD简介与Demo[5110:334355] {number = 3, name = (null)}开始执行 3 times
 2017-08-22 19:58:09.963 GCD简介与Demo[5110:334355] {number = 3, name = (null)}开始执行 4 times
 2017-08-22 19:58:13.969 GCD简介与Demo[5110:334355] {number = 3, name = (null)}开始执行 5 times
 2017-08-22 19:58:18.972 GCD简介与Demo[5110:334292] 回到主线程操作
 */

可以看出,串行队列中,所有的任务按照顺序依次执行,执行完毕后,再执行后面的代码

4.GCD队列组dispatch_group_t

应用场景:多个任务并发执行,我们需要获取到全部执行完毕后的时间节点(使用dispatch_group_notify获取)

- (void)dispatchGroup{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("并行队列名", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        NSLog(@"执行完第一个任务");
    });
    dispatch_group_async(group, queue, ^{
        sleep(1);
        NSLog(@"执行完第二个任务");
    });
    dispatch_group_async(group, queue, ^{
        sleep(2);
        NSLog(@"执行完第三个任务");
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"任务全部执行完毕");
    });
}
/*
 2017-08-22 20:06:04.304 GCD简介与Demo[5155:341594] 执行完第一个任务
 2017-08-22 20:06:05.306 GCD简介与Demo[5155:341596] 执行完第二个任务
 2017-08-22 20:06:06.306 GCD简介与Demo[5155:341593] 执行完第三个任务
 2017-08-22 20:06:06.306 GCD简介与Demo[5155:341593] 任务全部执行完毕
 */

五、GCD使用信号量控制并发dispatch_semaphore

1.函数讲解:

  • dispatch_semaphore_create

函数原型:dispatch_samaphore_t dispatch_semaphore_create(long value)

传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量。 值得注意的是,这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。

  • dispatch_semaphore_signal

函数原型:long dispatch_semaphore_signal(dispatch_semaphore_tdsema)

这个函数会使传入的信号量dsema的值加1

当返回值为0时表示当前并没有线程等待其处理的信号量,其处理的信号量的值加1即可。当返回值不为0时,表示其当前有(一个或多个)线程等待其处理的信号量,并且该函数唤醒了一个等待的线程(当线程有优先级时,唤醒优先级最高的线程;否则随机唤醒)。

  • dispatch_semaphore_wait

函数原型: long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)

这个函数会使传入的信号量dsema的值减1

如果desema的值为0,那么这个函数就阻塞当前线程等待timeout(注意timeout的类型为dispatch_time_t)

不能直接传入整形或float型数,如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1。

如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句。

dispatch_semaphore_wait的返回值也为long型。当其返回0时表示在timeout之前,该函数所处的线程被成功唤醒。当其返回不为0时,表示timeout发生。

2.timeout参数

  • DISPATCH_TIME_NOW 表示当前(立刻超时)

  • DISPATCH_TIME_FOREVER  表示遥远的未来(永不超时)

具体可参照:关于dispatch_semaphore的使用

- (void)dispatchSemaphore{
    //创建信号,设置最大并发数为2
    dispatch_semaphore_t sem = dispatch_semaphore_create(2);
    for (int i = 0; i < 10; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            long a = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
            NSLog(@"第%d个任务执行,此时a为:%ld",i+1,a);
            sleep(1);
            long b = dispatch_semaphore_signal(sem);
            NSLog(@"b为:%ld",b);
        });
    }
}
/*
 结果
 2017-08-22 21:11:48.928 GCD简介与Demo[716:12343] 第2个任务执行
 2017-08-22 21:11:48.928 GCD简介与Demo[716:12330] 第1个任务执行
 2017-08-22 21:11:49.931 GCD简介与Demo[716:12329] 第4个任务执行
 2017-08-22 21:11:49.931 GCD简介与Demo[716:12332] 第3个任务执行
 2017-08-22 21:11:50.933 GCD简介与Demo[716:12345] 第5个任务执行
 2017-08-22 21:11:50.933 GCD简介与Demo[716:12346] 第6个任务执行
 2017-08-22 21:11:51.937 GCD简介与Demo[716:12347] 第7个任务执行
 2017-08-22 21:11:51.937 GCD简介与Demo[716:12348] 第8个任务执行
 2017-08-22 21:11:52.940 GCD简介与Demo[716:12350] 第10个任务执行
 2017-08-22 21:11:52.940 GCD简介与Demo[716:12349] 第9个任务执行
 */

可以看出,同时执行任务的只有两个,这就控制了最大并发数

六、GCD定时器 dispatch_source_set_timer

NSTimer是我们最为熟悉的定时器,但是NSTimer有很大的缺点,并不准确。而GCD定时器,则是严格按照规定好的规格去做事,它更为准确

NSTimer是在RunLoop的基础上执行的,而RunLoop是在GCD的基础上实现的,所以GCD可以算是更为高级

@interface ViewController ()
//这里不需要*号,因为dispatch_source_t实现中已经加进了指针*号
@property (nonatomic,strong) dispatch_source_t timer;

@property (nonatomic,strong) UIView *animationView;

@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _animationView = [[UIView alloc]initWithFrame:CGRectMake(0, self.view.bounds.size.height/2.-50, 100, 100)];
    _animationView.backgroundColor = [UIColor redColor];
    [self.view addSubview:_animationView];
    [self beginGCDTimer];
}
- (void)beginGCDTimer{
    //如果定时器已经开启,则将它关闭,并置空
    if (self.timer) {
        dispatch_source_cancel(self.timer);
        self.timer = nil;
    }
    //创建队列
    dispatch_queue_t queue = dispatch_queue_create("并行队列名", DISPATCH_QUEUE_CONCURRENT);
    //创建定时器
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    //设置开始时间 后面的参数意思为从现在开始后2秒开始执行定时器回调
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC);
    //设置时间间隔 uint64_t为unsigned long long的别名,无符号长长整形
    uint64_t interval = 0.01 * NSEC_PER_SEC;
    //设置定时器 最后一个参数为偏差,一般设置为0
    dispatch_source_set_timer(_timer, start, interval, 0);
    //设置回调
    dispatch_source_set_event_handler(_timer, ^{
        CGFloat x = self.animationView.frame.origin.x;
        CGFloat width = self.view.bounds.size.width;
        x += 1;
        if (x >= width) {
            x = 0;
        }
        CGRect frame = self.animationView.frame;
        frame.origin.x = x;
        dispatch_async(dispatch_get_main_queue(), ^{
            self.animationView.frame = frame;
        });
    });
    //定时器默认是暂停的,需要手动启动
    dispatch_resume(_timer);
    //如果要退出GCD的定时器可以使用
    //dispatch_source_cancel(_timer);
}
@end

注意,如果需要暂停GCD定时器,则调用dispatch_suspend(_timer);恢复则调用dispatch_resume(_timer);

NSTimer不准时的原因:
1.RunLoop循环处理总会有时间
2.受到RunLoop模式的影响

GCD Timer与NSTimer是不同的
1.都是源,前者是Dispatch的源,后者是RunLoop的源
2.GCD Timer不需要像NSTimer那样加入RunLoop的mode

GCD Timer总结
1.GCD Timer精度高(纳米级别)
2.GCD Timer在主线程执行会受RunLoop影响,子线程不受影响
3.GCD Timer不受模式切换的影响

备注:

关于时间的一些宏

#define NSEC_PER_SEC 1000000000ull

#define USEC_PER_SEC 1000000ull

#define NSEC_PER_USEC 1000ull

NSEC:纳秒。

USEC:微秒。

SEC:秒

PER:每

1 、NSEC_PER_SEC,每秒有多少纳秒。

2 、USEC_PER_SEC,每秒有多少毫秒。(注意是指在纳秒的基础上)

3 、NSEC_PER_USEC,每毫秒有多少纳秒。

七、dispatch_source_t

dispatch_source_t还有更多更多的用法,我们介绍最常用的一种,使用DISPATCH_SOURCE_TYPE_DATA_ADD,我们可以使用联结来实现一些神奇的功能

联结的大致流程:在任一线程上调用它的一个函数 dispatch_source_merge_data 后,会执行 Dispatch Source 事先定义好的句柄,而且我们可以在这个句柄中使用dispatch_source_get_data获取到dispatch_source_merge_data传递给我们的值

下面是一个简单的示例:

- (void)viewDidLoad{
    [super viewDidLoad];
    self.queue = dispatch_queue_create("add", NULL);
    self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, self.queue);
    dispatch_source_set_event_handler(self.source, ^{
        unsigned long data = dispatch_source_get_data(self.source);
        NSLog(@"响应,得到值为:%lu",data);
    });
    dispatch_resume(self.source);
    
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    dispatch_source_merge_data(self.source, 1);
}

从上面示例我们可以想到哪个使用场景呢?
在我们分片下载一个大文件时,我们可以把各个分片在异步并发下载,下载时使用dispatch_source_merge_data回传我们的进度,然后再句柄回调中将各个进度相加,这就可以计算出我们的下载总进度了

更多关于dispatch_source_t的用法可以参考大神的iOS多线程——Dispatch Source

你可能感兴趣的:(GCD基本用法大全)