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 <Foundation/Foundation.h>
@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 <Foundation/Foundation.h>
@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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • 一、多线程简介: 所谓多线程是指一个 进程 -- process(可以理解为系统中正在运行的一个应用程序)中可以开...
    寻形觅影阅读 1,030评论 0 6
  • 1.多线程知识: 线程与队列的区别:线程是代码执行的路径,队列则是用于保存以及管理任务的,线程负责去队列中取任务进...
    一川烟草i蓑衣阅读 320评论 0 0
  • 你写诗 我写你 你就是我的诗
    默o阅读 99评论 0 0
  • 我喜欢你,过往,离去 我喜欢你,随风,亦是 喜欢你,在风里,在樱花开的慢荫里 喜欢你,踏燕而去 我曾牵着风等你, ...
    纤音阅读 166评论 0 1
  • 去年初,思维导图的百度指数只有1000,短短一年,增长了3倍,看来思维导图越来越火了。思维导图的火爆,与一个人有着...
    王通专栏阅读 428评论 0 0