GCD(Grand central Dispatch)是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。下面我讲讲述关于GCD的点,通篇读完大约10-20分钟。

一、为什么要用GCD?

GCD是iOS线程的一种,也是被经常使用的一种方式。GCD也有很多的好处:

(1)GCD可用于多核的并行运算;

(2)GCD会自动利用更多的CPU内核;

(3)GCD会自动管理线程的生命周期;

(4)程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

二、GCD任务和队列

先了解GCD中两个核心概念:任务和队列。

1.任务:就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在GCD中是放在block中的。执行任务有两种方式:同步执行(sync)和异步执行(async)。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。

1).同步执行(sync):
同步添加到指定的队列中,在添加的任务执行结束之前,会一直等待,知道队列里面的任务完成之后再继续执行。

只能在当前线程中执行任务,不具备开启新线程的能力。

2).异步执行(async):

异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。

可以在新的线程中执行任务,具备开启新线程的能力。

注意: 异步执行(async) 虽然具有开启新线程的能力,但是并不一定开启新线程。这跟任务所指定的队列类型有关(下面会讲)。

2.队列:这里的队列指的是执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。

在GCD中有两种队列:串行队列和并发队列。两者都符合FIFO(先进先出)的原则。两者的主要区别是:执行的顺序不同,以及开启线程数不同。

1)串行队列:

每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)

2)并发队列:

可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)

注意:并发队列 的并发功能只有在异步(dispatch_async)函数下才有效

三、GCD的使用步骤

GCD的使用步骤其实比较简单,只有两步

1.创建一个队列(串行队列或者并行队列)

2.将任务追加到任务的等待队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)

3.1队列的创建方法/获取方法

可以使用dispatch_queue_create来创建队列,需要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空,Dispatch Queue的名称推荐使用应用程序ID这种逆序全程域名,第二个参数用来表示串行队列还是并发队列。DISPATCH_QUEUE_SERIAL 表示串行队列,DISPATCH_QUEUE_CONCURRENT表示并发队列。

下面是创建队列的例子:

  1. // 串行队列的创建方法
  2. dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
  3. // 并发队列的创建方法
  4. dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

对于串行队列,GCD提供了一种特殊的串行队列:主队列(Main Dispatch Queue)

1)所有放在主队列的任务,都会放到主线程中执行。

2)可使用dispatch_get_main_queue()获得主队列。

  1. // 主队列的获取方法
  2. dispatch_queue_t queue = dispatch_get_main_queue();

对于并发队列,GCD默认提供了全局并发队列(Global Dispatch Queue)

可以使用dispatch_get_global_queue来获取。需要传入两个参数。第一个参数是队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,可以用0即可。

  1. // 全局并发队列的获取方法
  2. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );

 3.2任务的创建方法

GCD提供了同步执行任务的创建方法dispatch_sync和异步执行任务创建方法dispatch_async。

  1. // 同步执行任务创建方法
  2. dispatch_sync(queue, ^{
  3. // 这里放同步执行任务代码
  4. });
  5. // 异步执行任务创建方法
  6. dispatch_async(queue, ^{
  7. // 这里放异步执行任务代码
  8. });

虽然使用GCD只需要两步,但是既然我们有两种队列(串行队列/并行队列),两种任务执行方式(同步执行/异步执行)那么我们就有了四种不同的组合方式。这四种不同的方式为:

1.同步执行 + 并发队列

2.异步执行 + 并发队列

3.同步执行 + 串行队列

4.异步执行 + 串行队列

实际上,刚才说了两种特殊队列:全局并发队列、主队列。全局并发队列可以作为普通并发队列来使用,但是主队列因为有点特殊,我们就又多了两种组合方式。我们就有了六种不同的方式了。

5.同步执行 + 主队列

6.异步执行 + 主队列

那么这几种不同组合方式各有什么区别呢,这里为了方便,先上结果,再来讲解。

四、GCD的基本使用

4.1 同步执行+并发队列

在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。

  1. (void)syncConcurrent {
  2. NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
  3. NSLog(@"syncConcurrent---begin");
  4.  
  5. dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
  6.  
  7. dispatch_sync(queue, ^{
  8. // 追加任务1
  9. for (int i = ; i < ; ++i) {
  10. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  11. NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
  12. }
  13. });
  14.  
  15. dispatch_sync(queue, ^{
  16. // 追加任务2
  17. for (int i = ; i < ; ++i) {
  18. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  19. NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
  20. }
  21. });
  22.  
  23. dispatch_sync(queue, ^{
  24. // 追加任务3
  25. for (int i = ; i < ; ++i) {
  26. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  27. NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
  28. }
  29. });
  30.  
  31. NSLog(@"syncConcurrent---end");

打印输出结果:

  1. 输出结果:
  2.  
  3. -- ::55.095932+ YSC-GCD-demo[:] currentThread---{number = , name = main}
  4.  
  5. -- ::55.096086+ YSC-GCD-demo[:] syncConcurrent---begin
  6.  
  7. -- ::57.097589+ YSC-GCD-demo[:] ---{number = , name = main}
  8.  
  9. -- ::59.099100+ YSC-GCD-demo[:] ---{number = , name = main}
  10.  
  11. -- ::01.099843+ YSC-GCD-demo[:] ---{number = , name = main}
  12.  
  13. -- ::03.101171+ YSC-GCD-demo[:] ---{number = , name = main}
  14.  
  15. -- ::05.101750+ YSC-GCD-demo[:] ---{number = , name = main}
  16.  
  17. -- ::07.102414+ YSC-GCD-demo[:] ---{number = , name = main}
  18.  
  19. -- ::07.102575+ YSC-GCD-demo[:] syncConcurrent---end

从结果可以看到:

(1)所有任务都是在当前线程(主线程)中执行的,没有开启新的线程(同步执行不具备开启新线程的能力)。

(2)所有任务都在打印的syncConcurrent---begin和syncConcurrent---end之间执行的(同步任务需要等待队列的任务执行结束)。

(3)任务按顺序执行的。按顺序执行的原因:虽然并发队列可以开启多个线程,并且同时执行多个任务。但是因为本身不能创建新线程,只有当前线程这一个线程(同步任务不具备开启新线程的能力),所以也就不存在并发。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后,才能继续接着执行下面的操作(同步任务需要等待队列的任务执行结束)。所以任务只能一个接一个按顺序执行,不能同时被执行。

4.2 异步执行+并发队列

可以开启多个线程,任务交替(同时)执行

  1. - (void)asyncConcurrent {
  2. NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
  3. NSLog(@"asyncConcurrent---begin");
  4.  
  5. dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
  6.  
  7. dispatch_async(queue, ^{
  8. // 追加任务1
  9. for (int i = ; i < ; ++i) {
  10. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  11. NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
  12. }
  13. });
  14.  
  15. dispatch_async(queue, ^{
  16. // 追加任务2
  17. for (int i = ; i < ; ++i) {
  18. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  19. NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
  20. }
  21. });
  22.  
  23. dispatch_async(queue, ^{
  24. // 追加任务3
  25. for (int i = ; i < ; ++i) {
  26. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  27. NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
  28. }
  29. });
  30.  
  31. NSLog(@"asyncConcurrent---end");
  32. }

结果如下:

  1. 输出结果:
  2.  
  3. -- ::41.769269+ YSC-GCD-demo[:] currentThread---{number = , name = main}
  4.  
  5. -- ::41.769496+ YSC-GCD-demo[:] asyncConcurrent---begin
  6.  
  7. -- ::41.769725+ YSC-GCD-demo[:] asyncConcurrent---end
  8.  
  9. -- ::43.774442+ YSC-GCD-demo[:] ---{number = , name = (null)}
  10.  
  11. -- ::43.774440+ YSC-GCD-demo[:] ---{number = , name = (null)}
  12.  
  13. -- ::43.774440+ YSC-GCD-demo[:] ---{number = , name = (null)}
  14.  
  15. -- ::45.779286+ YSC-GCD-demo[:] ---{number = , name = (null)}
  16.  
  17. -- ::45.779302+ YSC-GCD-demo[:] ---{number = , name = (null)}
  18.  
  19. -- ::45.779286+ YSC-GCD-demo[:] ---{number = , name = (null)}

在异步执行+并发队列可以看出:

除了当前线程(主线程),系统又开启了3个线程,并且任务是交替/同时执行的。(异步执行具备开启新线程的能力。且并发队列可开启多个线程,同时执行多个任务)。所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才执行的。说明当前线程没有等待,而是直接开启了新线程,在新线程中执行任务(异步执行不做等待,可以继续执行任务)。

4.3 同步执行 + 串行队列

不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务。

  1. - (void)syncSerial {
  2. NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
  3. NSLog(@"syncSerial---begin");
  4.  
  5. dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
  6.  
  7. dispatch_sync(queue, ^{
  8. // 追加任务1
  9. for (int i = ; i < ; ++i) {
  10. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  11. NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
  12. }
  13. });
  14. dispatch_sync(queue, ^{
  15. // 追加任务2
  16. for (int i = ; i < ; ++i) {
  17. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  18. NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
  19. }
  20. });
  21. dispatch_sync(queue, ^{
  22. // 追加任务3
  23. for (int i = ; i < ; ++i) {
  24. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  25. NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
  26. }
  27. });
  28.  
  29. NSLog(@"syncSerial---end");
  30. }

输出结果:

  1. -- ::37.876811+ YSC-GCD-demo[:] currentThread---{number = , name = main}
  2.  
  3. -- ::37.876998+ YSC-GCD-demo[:] syncSerial---begin
  4.  
  5. -- ::39.878316+ YSC-GCD-demo[:] ---{number = , name = main}
  6.  
  7. -- ::41.879829+ YSC-GCD-demo[:] ---{number = , name = main}
  8.  
  9. -- ::43.880660+ YSC-GCD-demo[:] ---{number = , name = main}
  10.  
  11. -- ::45.881265+ YSC-GCD-demo[:] ---{number = , name = main}
  12.  
  13. -- ::47.882257+ YSC-GCD-demo[:] ---{number = , name = main}
  14.  
  15. -- ::49.883008+ YSC-GCD-demo[:] ---{number = , name = main}
  16.  
  17. -- ::49.883253+ YSC-GCD-demo[:] syncSerial---end

在同步执行+串行队列可以看出

(1)所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(同步执行不具备开启新线程的能力)。

(2)所有任务都在打印的syncConcurrent---begin和syncConcurrent---end之间执行(同步任务需要等待队列的任务执行结束)。

(3)任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。

4.4 异步执行 + 串行队列

会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务

  1. - (void)asyncSerial {
  2. NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
  3. NSLog(@"asyncSerial---begin");
  4.  
  5. dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
  6.  
  7. dispatch_async(queue, ^{
  8. // 追加任务1
  9. for (int i = ; i < ; ++i) {
  10. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  11. NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
  12. }
  13. });
  14. dispatch_async(queue, ^{
  15. // 追加任务2
  16. for (int i = ; i < ; ++i) {
  17. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  18. NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
  19. }
  20. });
  21. dispatch_async(queue, ^{
  22. // 追加任务3
  23. for (int i = ; i < ; ++i) {
  24. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  25. NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
  26. }
  27. });
  28.  
  29. NSLog(@"asyncSerial---end");
  30. }

执行结果如下:

  1. -- ::17.029999+ YSC-GCD-demo[:] currentThread---{number = , name = main}
  2.  
  3. -- ::17.030212+ YSC-GCD-demo[:] asyncSerial---begin
  4.  
  5. -- ::17.030364+ YSC-GCD-demo[:] asyncSerial---end
  6.  
  7. -- ::19.035379+ YSC-GCD-demo[:] ---{number = , name = (null)}
  8.  
  9. -- ::21.037140+ YSC-GCD-demo[:] ---{number = , name = (null)}
  10.  
  11. -- ::23.042220+ YSC-GCD-demo[:] ---{number = , name = (null)}
  12.  
  13. -- ::25.042971+ YSC-GCD-demo[:] ---{number = , name = (null)}
  14.  
  15. -- ::27.047690+ YSC-GCD-demo[:] ---{number = , name = (null)}
  16.  
  17. -- ::29.052327+ YSC-GCD-demo[:] ---{number = , name = (null)}

从执行结果看:

(1)开启了一条新线程(异步执行具备开启新线程的能力,串行队列只开启一个线程)。

(2)所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。

(3)任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。

下边讲讲刚才我们提到过的特殊队列:主队列。

主队列:GCD自带的一种特殊的串行队列

所有放在主队列中的任务,都会放到主线程中执行

可使用dispatch_get_main_queue()获得主队列

我们再来看看主队列的两种组合方式。

4.5 同步执行 + 主队列

同步执行 + 主队列在不同线程中调用结果也是不一样,在主线程中调用会出现死锁,而在其他线程中则不会。

  1. /**
  2. * 同步执行 + 主队列
  3. * 特点(主线程调用):互等卡主不执行。
  4. * 特点(其他线程调用):不会开启新线程,执行完一个任务,再执行下一个任务。
  5. */
  6. - (void)syncMain {
  7.  
  8. NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
  9. NSLog(@"syncMain---begin");
  10.  
  11. dispatch_queue_t queue = dispatch_get_main_queue();
  12.  
  13. dispatch_sync(queue, ^{
  14. // 追加任务1
  15. for (int i = ; i < ; ++i) {
  16. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  17. NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
  18. }
  19. });
  20.  
  21. dispatch_sync(queue, ^{
  22. // 追加任务2
  23. for (int i = ; i < ; ++i) {
  24. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  25. NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
  26. }
  27. });
  28.  
  29. dispatch_sync(queue, ^{
  30. // 追加任务3
  31. for (int i = ; i < ; ++i) {
  32. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  33. NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
  34. }
  35. });
  36.  
  37. NSLog(@"syncMain---end");
  38. }

输出结果:

  1. -- ::36.842892+ YSC-GCD-demo[:] currentThread---{number = , name = main}
  2.  
  3. -- ::36.843050+ YSC-GCD-demo[:] syncMain---begin
  4.  
  5. (lldb)

在同步执行 + 主队列可以惊奇的发现:

在主线程中使用同步执行 + 主队列,追加到主线程的任务1、任务2、任务3都不再执行了,而且syncMain---end也没有打印,在XCode 9上还会报崩溃。这是为什么呢?

这是因为我们在主线程中执行syncMain方法,相当于把syncMain任务放到了主线程的队列中。而同步执行会等待当前队列中的任务执行完毕,才会接着执行。那么当我们把任务1追加到主队列中,任务1就在等待主线程处理完syncMain任务。而syncMain任务需要等待任务1执行完毕,才能接着执行。

那么,现在的情况就是syncMain任务和任务1都在等对方执行完毕。这样大家互相等待,所以就卡住了,所以我们的任务执行不了,而且syncMain---end也没有打印。

要是如果不在主线程中调用,而在其他线程中调用会如何呢

4.5.2 在其他线程中调用同步执行 + 主队列

  1. // 使用 NSThread 的 detachNewThreadSelector 方法会创建线程,并自动启动线程执行
  2. selector 任务
  3. [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];

输出结果如下:

  1. -- ::19.377321+ YSC-GCD-demo[:] currentThread---{number = , name = (null)}
  2.  
  3. -- ::19.377494+ YSC-GCD-demo[:] syncMain---begin
  4.  
  5. -- ::21.384716+ YSC-GCD-demo[:] ---{number = , name = main}
  6.  
  7. -- ::23.386091+ YSC-GCD-demo[:] ---{number = , name = main}
  8.  
  9. -- ::25.387687+ YSC-GCD-demo[:] ---{number = , name = main}
  10.  
  11. -- ::27.388648+ YSC-GCD-demo[:] ---{number = , name = main}
  12.  
  13. -- ::29.390459+ YSC-GCD-demo[:] ---{number = , name = main}
  14.  
  15. -- ::31.391965+ YSC-GCD-demo[:] ---{number = , name = main}
  16.  
  17. -- ::31.392513+ YSC-GCD-demo[:] syncMain---end

在其他线程中使用同步执行 + 主队列可看到:

(1)所有任务都是在主线程(非当前线程)中执行的,没有开启新的线程(所有放在主队列中的任务,都会放到主线程中执行)。

(2)所有任务都在打印的syncConcurrent---begin和syncConcurrent---end之间执行(同步任务需要等待队列的任务执行结束)。

(3)任务是按顺序执行的(主队列是串行队列,每次只有一个任务被执行,任务一个接一个按顺序执行)。

为什么现在就不会卡住了呢?

因为syncMain 任务放到了其他线程里,而任务1、任务2、任务3都在追加到主队列中,这三个任务都会在主线程中执行。syncMain 任务在其他线程中执行到追加任务1到主队列中,因为主队列现在没有正在执行的任务,所以,会直接执行主队列的任务1,等任务1执行完毕,再接着执行任务2、任务3。所以这里不会卡住线程。

4.6 异步执行 + 主队列

只在主线程中执行任务,执行完一个任务,再执行下一个任务。

  1. - (void)asyncMain {
  2. NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
  3. NSLog(@"asyncMain---begin");
  4.  
  5. dispatch_queue_t queue = dispatch_get_main_queue();
  6.  
  7. dispatch_async(queue, ^{
  8. // 追加任务1
  9. for (int i = ; i < ; ++i) {
  10. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  11. NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
  12. }
  13. });
  14.  
  15. dispatch_async(queue, ^{
  16. // 追加任务2
  17. for (int i = ; i < ; ++i) {
  18. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  19. NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
  20. }
  21. });
  22.  
  23. dispatch_async(queue, ^{
  24. // 追加任务3
  25. for (int i = ; i < ; ++i) {
  26. [NSThread sleepForTimeInterval:]; // 模拟耗时操作
  27. NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
  28. }
  29. });
  30.  
  31. NSLog(@"asyncMain---end");
  32. }

执行结果如下:

  1. -- ::49.981505+ YSC-GCD-demo[:] currentThread---{number = , name = main}
  2.  
  3. -- ::49.981935+ YSC-GCD-demo[:] asyncMain---begin
  4.  
  5. -- ::49.982352+ YSC-GCD-demo[:] asyncMain---end
  6.  
  7. -- ::51.991096+ YSC-GCD-demo[:] ---{number = , name = main}
  8.  
  9. -- ::53.991959+ YSC-GCD-demo[:] ---{number = , name = main}
  10.  
  11. -- ::55.992937+ YSC-GCD-demo[:] ---{number = , name = main}
  12.  
  13. -- ::57.993649+ YSC-GCD-demo[:] ---{number = , name = main}
  14.  
  15. -- ::59.994928+ YSC-GCD-demo[:] ---{number = , name = main}
  16.  
  17. -- ::01.995589+ YSC-GCD-demo[:] ---{number = , name = main}

在异步执行 + 主队列可以看到:

(1)所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然异步执行具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中)。

(2)所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。

(3)任务是按顺序执行的(因为主队列是串行队列,每次只有一个任务被执行,任务一个接一个按顺序执行)。

对于上面是GCD的基本介绍,下一篇将讲述GCD的使用,谢谢观看!!!

iOS多线程(上)——GCD详解(上)的更多相关文章

  1. Swift - 多线程GCD详解

    //  GCD详解 //  目录: //  1. 创建GCD队列(最常用) //  2. 自定义创建队列 //  3. 使用多线程实现延迟加载 //  4. 使用多线程实现重复(循环) //  5. ...

  2. Multipart/form-data POST文件上传详解

    Multipart/form-data POST文件上传详解 理论 简单的HTTP POST 大家通过HTTP向服务器发送POST请求提交数据,都是通过form表单提交的,代码如下: <form ...

  3. [转]人人网首页拖拽上传详解(HTML5 Drag&Drop、FileReader API、formdata)

    人人网首页拖拽上传详解(HTML5 Drag&Drop.FileReader API.formdata) 2011年12月11日 | 彬Go 上一篇:给力的 Google HTML5 训练营( ...

  4. Multipart/form-data POST文件上传详解(转)

    Multipart/form-data POST文件上传详解 理论 简单的HTTP POST 大家通过HTTP向服务器发送POST请求提交数据,都是通过form表单提交的,代码如下: <form ...

  5. IE8“开发人员工具”使用详解上(各级菜单详解)

    来源: http://www.cnblogs.com/JustinYoung/archive/2009/03/24/kaifarenyuangongju.html IE8“开发人员工具”使用详解上(各 ...

  6. C++框架_之Qt的窗口部件系统的详解-上

    C++框架_之Qt的窗口部件系统的详解-上 第一部分概述 第一次建立helloworld程序时,曾看到Qt Creator提供的默认基类只有QMainWindow.QWidget和QDialog三种. ...

  7. ArrayList, LinkedList, Vector - dudu:史上最详解

    ArrayList, LinkedList, Vector - dudu:史上最详解 我们来比较一下ArrayList, LinkedLIst和Vector它们之间的区别.BZ的JDK版本是1.7.0 ...

  8. [js高手之路]深入浅出webpack教程系列2-配置文件webpack.config.js详解(上)

    [js高手之路]深入浅出webpack教程系列索引目录: [js高手之路]深入浅出webpack教程系列1-安装与基本打包用法和命令参数 [js高手之路]深入浅出webpack教程系列2-配置文件we ...

  9. Newtonsoft.Json C# Json序列化和反序列化工具的使用、类型方法大全 C# 算法题系列(二) 各位相加、整数反转、回文数、罗马数字转整数 C# 算法题系列(一) 两数之和、无重复字符的最长子串 DateTime Tips c#发送邮件,可发送多个附件 MVC图片上传详解

    Newtonsoft.Json C# Json序列化和反序列化工具的使用.类型方法大全   Newtonsoft.Json Newtonsoft.Json 是.Net平台操作Json的工具,他的介绍就 ...

  10. MVC图片上传详解 IIS (安装SSL证书后) 实现 HTTP 自动跳转到 HTTPS C#中Enum用法小结 表达式目录树 “村长”教你测试用例 引用provinces.js的三级联动

    MVC图片上传详解   MVC图片上传--控制器方法 新建一个控制器命名为File,定义一个Img方法 [HttpPost]public ActionResult Img(HttpPostedFile ...

随机推荐

  1. android踩坑日记1

    Android四大组件-活动.服务.广播.碎片 情况一 应用场景:定时从服务器获取数据,然后活动或者碎片中根据最新获得的数据,更新UI. 思考: 首先定时,想到定时器,推荐使用系统自带的AlertMa ...

  2. mybatis注解SQL

    在网上找了很久,特别是批量插入,很久都没有找到,终于最后一不小心就搞出来了.所以想写个随笔保存下来,一方面想提高自己的总结能力,一方面为了结识有相同兴趣的朋友(第一篇博客我的天纳

  3. noip第32课资料

  4. 面向对象一 OOP与类

    OOP面向对象 面向对象与面向过程 面向过程让计算机有步骤的顺次做一件事情,是一种过程化的叙事思维:面向对象是一种计算机世界里解决复杂软件工程的方法论,拆解问题复杂度,从人类思维角度提出解决问题的步骤 ...

  5. 运维工具pssh和pdsh安装和使用

    1. pssh安装与使用 1.1 pssh安装 [root@server]# wget http://peak.telecommunity.com/dist/ez_setup.py [root@ser ...

  6. tomcat服务器怎样远程调试

    适合windows系统 1.首先tomcat/bin目录下startup.bat打开最前面添加以下代码: SET CATALINA_OPTS=-server -Xdebug -Xnoagent -Dj ...

  7. mysql命令行创建表,插入表数据

    create table t_hero( id int unsigned auto_increment primary key, name varchar(10) unique not null, a ...

  8. 3D轮播图

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. Docker学习笔记-磁盘挂载运行.netcore

    前言: 环境:centos7.5 64 位 正文: 首先我们在宿主机上安装 .NET Core SDK sudo rpm --import https://packages.microsoft.com ...

  10. Date相关

    处理时间是常见的需求,总结下Date类的相关知识 构建时间对象 Date 对象基于1970年1月1日(世界标准时间)起的毫秒数. 构建对象实例有多种方式: 不传入参数,默认以系统当前时间返回一个时间对 ...