GCD基础回顾,大神也不敢说全都记得

2017-07-22  本文已影响0人  褪而未变

1.GCD简介

什么是GCD

GCD的优势

GCD的核心

GCD使用的两个步骤

2.GCD的核心及基本语法

任务添加到队列分开写

/// 队列+任务
- (void)gcdDemo1
{
    // 全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    // 任务
    void (^task)() = ^ {
        NSLog(@"%@",[NSThread currentThread]);
    };

    // 同步任务
//    dispatch_sync(queue, task);

    // 异步任务 : 每次执行任务的线程不一定是一样的
    dispatch_async(queue, task);

    NSLog(@"end");
}

简写

- (void)gcdDemo2
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
}

线程间通信

/// 线程间通信
- (void)gcdDemo3
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"下载中... %@",[NSThread currentThread]);

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"下载完成 %@",[NSThread currentThread]);
        });
    });
}

使用GCD的线程间通信实现异步下载网络图片

- (void)downloadImage
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 获取网络图片地址
        NSURL *url = [NSURL URLWithString:@"http://photocdn.sohu.com/20151209/mp47379110_1449633737507_2_th.png"];
        // 获取网络图片二进制数据
        NSData *data = [NSData dataWithContentsOfURL:url];
        // 获取图片对象
        UIImage *image = [UIImage imageWithData:data];

        // 图片下载完成之后,回到主线程更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            // 设置图片视图
            self.imageView.image = image;
            // 图片视图自适应图片的大小
            [self.imageView sizeToFit];
            // 设置滚动视图
            [self.scrollView setContentSize:image.size];
        });
    });
}

3.GCD 与 NSThread 的对比

4.GCD中的任务

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
* queue:队列
* block:任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
* queue:队列
* block:任务

5.GCD的队列

(1)串行队列(Serial Dispatch Queue)

* 让任务一个接着一个`有序的执行`:不管队列里面放的是什么任务.一个任务执行完毕后,再执行下一个任务.
* 同时只能调度一个任务执行.

特点

队列创建

// 参数1 : 队列的标示符
// 参数2 : 队列的属性.决定了队列是串行的还是并行的.
dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_SERIAL);

串行队列+同步任务

#pragma mark - 串行队列+同步任务
/*
    1. 没有开新线程
    2. 循环是顺序打印
    3. @"end"最后执行
 */
- (void)gcdDemo1
{
    // 串行队列
    dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_SERIAL);

    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });
    }

    NSLog(@"end");
}

串行队列+异步任务

#pragma mark - 串行队列+异步任务
/*
    1. 开一条新线程 : 因为队列是顺序调度任务,前一个任务执行完成以后才能调度后面的任务,开一条新线程就够了
    2. 循环是顺序打印
    3. @"end"不是在最后执行
 */
- (void)gcdDemo2
{
    // 串行队列
    dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_SERIAL);

    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });
    }

    NSLog(@"end");
}

(2)并发队列(Concurrent Dispatch Queue)

* 可以让多个任务`并发/同时`执行.自动开启多个线程同时执行多个任务.
* 同时可以调度多个任务执行
* 并发队列的并发功能只有内部的任务是异步任务时,才有效.

## 特点

队列创建

// 参数1 : 队列的标示符
// 参数2 : 队列的属性.决定了队列是串行的还是并行的.
dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_CONCURRENT);

并发队列+同步任务

#pragma mark - 并发队列+同步任务
/*
    1. 没有开新线程
    2. 循环顺序打印
    3. @"end"最后执行
 */
- (void)gcdDemo1
{
    // 并发队列
    dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_CONCURRENT);

    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });
    }

    NSLog(@"end");
}

并发队列+异步任务

#pragma mark - 并发队列+同步任务
/*
 1. 开启多条新线程
 2. 循环无序打印
 3. @"end"不是最后执行
 */
- (void)gcdDemo2
{
    // 并发队列
    dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_CONCURRENT);

    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });
    }

    NSLog(@"end");
}

(3)主队列

特点

主队列创建

dispatch_queue_t queue = dispatch_get_main_queue();

主队列+异步任务

#pragma mark - 主队列+异步任务
/*
    1. 执行顺序 : @"start"->@"end"->执行中...
    2. 没有开启新线程
 */
- (void)gcdDemo1
{
    // 主队列 : 程序一启动就创建好的,不需要创建
    dispatch_queue_t queue = dispatch_get_main_queue();

    NSLog(@"start");

    dispatch_async(queue, ^{
        NSLog(@"执行中...%@",[NSThread currentThread]);
    });

    NSLog(@"end");
}

主队列+同步任务=死锁

#pragma mark - 主队列+同步任务=死锁
/*
    1. 执行顺序 : @"start"   后面的任务被阻塞了
    2. 同步任务和主线程相互等待,造成线程死锁
 */
- (void)gcdDemo2
{
    dispatch_queue_t queue = dispatch_get_main_queue();

    NSLog(@"start");

    dispatch_sync(queue, ^{
        NSLog(@"执行中...%@",[NSThread currentThread]);
    });

    NSLog(@"end");
}

死锁解决办法

#pragma mark - 死锁解决办法
// 主队列中的同步任务放进子线程中,不使其阻塞主线程
- (void)gcdDemo3
{
    dispatch_async(dispatch_queue_create("ZJ", DISPATCH_QUEUE_CONCURRENT), ^{

        NSLog(@"start");

        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"执行中...%@",[NSThread currentThread]);
        });

        NSLog(@"end");
    });
}

(4)全局队列

全局队列 & 并发队列的区别

全局队列+异步任务

#pragma mark - 全局队列
// 执行效果跟并发队列一样的
- (void)gcdDemo
{
    // 全局队列,跟主队列一样不需要创建
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });

        /*
        dispatch_sync(queue, ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });
        */
    }

    NSLog(@"end");
}

参数

  1. 服务质量(队列对任务调度的优先级)/iOS 7.0 之前,是优先级

    • iOS 8.0及以后
      • QOS_CLASS_USER_INTERACTIVE 0x21, 用户交互(希望最快完成-不能用太耗时的操作)
      • QOS_CLASS_USER_INITIATED 0x19, 用户期望(希望快,也不能太耗时)
      • QOS_CLASS_DEFAULT 0x15, 默认(用来底层重置队列使用的,不是给程序员用的)
      • QOS_CLASS_UTILITY 0x11, 实用工具(专门用来处理耗时操作!)
      • QOS_CLASS_BACKGROUND 0x09, 后台
      • QOS_CLASS_UNSPECIFIED 0x00, 未指定,可以和iOS 7.0 适配
    • iOS 7.0及以前
      • DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
      • DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
      • DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
      • DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
  2. 为未来保留使用的,应该永远传入0

结论:如果要适配 iOS 7.0 & 8.0,使用以下代码:
dispatch_get_global_queue(0, 0);

(5)GCD队列和任务组合总结

(6)GCD同步任务的作用

建立依赖关系

- (void)GCDDemo1
{
    // 创建全局的并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    dispatch_sync(queue, ^{
        // 查看当前线程
        NSLog(@"登陆 %@",[NSThread currentThread]);
    });

    dispatch_sync(queue, ^{
        // 查看当前线程
        NSLog(@"付费 %@",[NSThread currentThread]);
    });

    dispatch_sync(queue, ^{
        // 查看当前线程
        NSLog(@"下载 %@",[NSThread currentThread]);
    });

    dispatch_async(dispatch_get_main_queue(), ^{
        // 查看当前线程
        NSLog(@"通知用户 %@",[NSThread currentThread]);
    });
}

依赖关系的优化

- (void)GCDDemo2
{
    // 创建全局的并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    dispatch_async(queue, ^{

        dispatch_sync(queue, ^{
            // 查看当前线程
            NSLog(@"登陆 %@",[NSThread currentThread]);
        });

        dispatch_sync(queue, ^{
            // 查看当前线程
            NSLog(@"付费 %@",[NSThread currentThread]);
        });

        dispatch_sync(queue, ^{
            // 查看当前线程
            NSLog(@"下载 %@",[NSThread currentThread]);
        });

        dispatch_async(dispatch_get_main_queue(), ^{
            // 查看当前线程
            NSLog(@"通知用户 %@",[NSThread currentThread]);
        });
    });
}

5.GCD延迟执行(after)

- (void)afterDemo1
{
    NSLog(@"开始");

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        // 延迟执行的代码
        NSLog(@"我来晚了吗?");
    });

    NSLog(@"结束");
}
- (void)afterDemo2
{
    NSLog(@"开始");

    /*
    参数1 : dispatch_time_t when,表示延迟的时间
    参数2 : dispatch_queue_t queue,表示任务执行的队列
    参数3 : dispatch_block_t block,表示线程要执行的任务
    */

    // 参数1 : 延迟多长时间,精确到纳秒
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));

    // 参数2 : 在哪个队列
//    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    // 参数3 : 执行哪个任务
    dispatch_block_t block = ^{
        // 延迟执行的代码
        NSLog(@"我来晚了吗? %@",[NSThread currentThread]);
    };

    // 延迟多少纳秒,在哪个队列中调度执行哪个任务
    dispatch_after(when, queue, block);

    NSLog(@"结束");
}

6.GCD一次性执行(once)

验证一次性执行的可靠性

- (void)onceDemo1
{
    NSLog(@"mark");

    static dispatch_once_t onceToken;

    NSLog(@"返回值 %ld",onceToken);

    dispatch_once(&onceToken, ^{
        NSLog(@"hello");
    });
}

异步并发任务中验证执行一次代码的安全性

- (void)onceDemo2
{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    // 多线程环境下测试安全性
    for (int i = 0; i < 1000; i++) {

        NSLog(@"%d",i);

        dispatch_async(queue, ^{

            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                NSLog(@"hello");
            });
        });
    }
}

7.单例设计模式

单例设计模式的特点

  1. 有一个全局访问点 (供全局实例化单例的类方法)
  2. 单例保存在静态存储区
  3. 在内存有且只有一份
  4. 生命周期跟APP一样长

懒汉式单例

所谓懒汉式单例,表示在使用时才会创建

+ (instancetype)sharedNetworkTool
{
    // 保存在静态存储区
    static id instance;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        instance = [[self alloc] init];
    });

    return instance;
}

饿汉式单例

所谓饿汉式单例,表示尽早地创建单例实例,可以利用 initialize 方法建立单例

  • initialize 会在类第一次被使用时调用
  • initialize 方法的调用是线程安全的
@implementation NetworkManager

// 声明静态对象
static id instance;

+ (void)initialize
{
    // 只会开辟一次内存空间,只会被实例化一次
    instance = [[self alloc] init];
}

// 饿汉式单例
+ (instancetype)sharedManager
{
    return instance;
}

@end
上一篇 下一篇

猜你喜欢

热点阅读