iOS多线程-GCD的常见用法

复习下线程的基础知识, 这里主要是参考文顶顶多线程篇复习写的。

1、线程间通信示例

从子线程回到主线程

#import "ViewController.h"

@interface ViewController ()

@property (strong, nonatomic) UIImageView *imageView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 20, 300, 300)];
    _imageView.backgroundColor = [UIColor redColor];
    [self.view addSubview:_imageView];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"donwload---%@", [NSThread currentThread]);
        // 1.子线程下载图片 图片地址,其实是一行
        NSString *urlStr = @"https://timgsa.baidu.com/timg?image&quality=80"
        @"&size=b9999_10000&sec=1559061649110&di=adfd3a30f3bb4868722529859d14ae9d"
@"&imgtype=0&src=http%3A%2F%2Fpic31.nipic.com%2F20130719%2F9885883_095141604000_2.jpg";
        
        NSURL *url = [NSURL URLWithString:urlStr];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];
        
        // 2.回到主线程设置图片
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"setting---%@ %@", [NSThread currentThread], image);
            self.imageView.image = image;
        });
    });
}

@end

2、延时执行

iOS常见的延时执行有2种方式

  • 调用NSObject的方法
// 2秒后再调用self的run方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];

// 取消之前设置self的run方法
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(run) object:nil];
  • 使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 2秒后异步执行这里的代码...
});

3、队列组

有这么1种需求
首先:分别异步执行2个耗时的操作
其次:等2个异步操作都执行完毕后,再回到主线程执行操作

如果想要快速高效地实现上述需求,可以考虑用队列组

dispatch_group_t group =  dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 等前面的异步操作都执行完毕后,回到主线程...
});

从网络上下载两张图片,把两张图片合并成一张最终显示在view上。

#import "ViewController.h"

@interface ViewController ()

@property (strong, nonatomic) UIImageView *imageView1;
@property (strong, nonatomic) UIImageView *imageView2;
@property (strong, nonatomic) UIImageView *imageView3;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _imageView1 = [[UIImageView alloc] initWithFrame:CGRectMake(10, 30, 150, 150)];
    _imageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(170, 30, 150, 150)];
    _imageView3 = [[UIImageView alloc] initWithFrame:CGRectMake(10, 200, 300, 200)];
    [self.view addSubview:_imageView1];
    [self.view addSubview:_imageView2];
    [self.view addSubview:_imageView3];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    //    图片1:http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg
    //    图片2:http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg
    
    //1.创建一个队列组
    dispatch_group_t group = dispatch_group_create();
    
    //2.开启一个任务下载图片1
    __block UIImage *image1 = nil;
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        image1 = [self imageWithUrl:@"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/"
                 @"sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"];
        NSLog(@"图片1下载完成---%@",[NSThread currentThread]);
    });
    
    //3.开启一个任务下载图片2
    __block UIImage *image2 = nil;
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        image2 = [self imageWithUrl:@"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/"
                  @"sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"];
        NSLog(@"图片2下载完成---%@",[NSThread currentThread]);
    });
    
    //同时执行下载图片1\下载图片2操作
    //4.等group中的所有任务都执行完毕, 再回到主线程执行其他操作
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"显示图片---%@",[NSThread currentThread]);
        self.imageView1.image = image1;
        self.imageView2.image = image2;
        
        //合并两张图片
        //注意最后一个参数是浮点数(0.0),不要写成0。
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
        [image1 drawInRect:CGRectMake(0, 0, 100, 100)];
        [image2 drawInRect:CGRectMake(100, 0, 100, 100)];
        self.imageView3.image = UIGraphicsGetImageFromCurrentImageContext();
        //关闭上下文
        UIGraphicsEndImageContext();
        
        NSLog(@"图片合并完成---%@",[NSThread currentThread]);
    });
    
}

//封装一个方法,传入一个url参数,返回一张网络上下载的图片
- (UIImage *)imageWithUrl:(NSString *)urlStr {
    NSURL *url = [NSURL URLWithString:urlStr];
    NSData *data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data];
    return image;
}

@end

4、信号量

dispatch_semaphore是GCD用来同步的一种方式,与他相关的共有三个函数
(1)创建一个信号量, value的参数value必须大于或等于0。

 dispatch_semaphore_t  dispatch_semaphore_create(long value);

(2)使传入的信号量dsema的值加1。

 dispatch_semaphore_t  dispatch_semaphore_create(long value);

(3)dispatch_semaphore_wait这个函数会使传入的信号量dsema的值减1;如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;如果desema的值为0,那么这个函数就阻塞当前线程等待timeout,如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,且该函数即dispatch_semaphore_wait所处线程获得了信号量,那么就继续向下执行并将信号量减1。如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句。

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

(4)设置timeout的宏

DISPATCH_TIME_NOW、DISPATCH_TIME_FOREVER

示例代码

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"开始执行");
    [NSThread sleepForTimeInterval:2];
    NSLog(@"signal");
    dispatch_semaphore_signal(semaphore);
});
NSLog(@"等待中");

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"得到信号量了");

5、一次性代码

使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只执行1次的代码(这里面默认是线程安全的)
});
//整个程序运行过程中,只会执行一次。

6、单例模式

  • @synchronized方式 加锁的方式比较慢
//.h文件
#import 
@interface SingleObject : NSObject
+ (instancetype)shareInstance;
@end
.m文件
#import "SingleObject.h"

@implementation SingleObject
static id _instance;

/**
 *  alloc方法内部会调用这个方法
 */
+ (id)allocWithZone:(struct _NSZone *)zone {
    if (_instance == nil) { // 防止频繁加锁
        @synchronized(self) {
            if (_instance == nil) { // 防止创建多次
                _instance = [super allocWithZone:zone];
            }
        }
    }
    return _instance;
}

+ (instancetype)shareInstance {
    if (_instance == nil) { // 防止频繁加锁
        @synchronized(self) {
            if (_instance == nil) { // 防止创建多次
                _instance = [[self alloc] init];
            }
        }
    }
    return _instance;
}

- (id)copyWithZone:(NSZone *)zone {
    return _instance;
}

@end
  • diapatch_once方式
//.h文件
#import 
@interface SingleObject : NSObject
+ (instancetype)shareInstance;
@end
#import "SingleObject.h"

@implementationSingleObject
// 用来保存唯一的单例对象
static id _instace;

+ (id)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instace = [super allocWithZone:zone];
    });
    return _instace;
}

+ (instancetype)shareInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instace = [[self alloc] init];
    });
    return _instace;
}

- (id)copyWithZone:(NSZone *)zone {
    return _instace;
}
@end

你可能感兴趣的:(iOS多线程-GCD的常见用法)