GCD队列组 -- dispatch_group_t
为了方便管理队列里的任务,把队列加到一个组里,当组中任务完成后,队列组会通过dispatch_group_notify通知用户.
- (void)viewDidLoad {
[super viewDidLoad];
//全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//创建group对象
dispatch_group_t group = dispatch_group_create();
//将任务提到group中
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"第一个图片下载完毕%@", [NSThread currentThread]);
});
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"第二个图片下载完毕:%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"第三个图片下载完毕:%@",[NSThread currentThread]);
});
//通知group中的任务执行完了(回到主线程)
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//三个图片已经全部下载完了(子线程)
NSLog(@"三个图片下载完毕:%@", [NSThread currentThread]);
});
}
执行结果 :
2015-11-16 19:21:03.130 pthread[1841:163756] 第三个图片下载完毕:<NSThread: 0x7fe34141f2d0>{number = 2, name = (null)}
2015-11-16 19:21:04.135 pthread[1841:163595] 第二个图片下载完毕:<NSThread: 0x7fe341505770>{number = 1, name = main}
2015-11-16 19:21:07.130 pthread[1841:163757] 第一个图片下载完毕<NSThread: 0x7fe34140b4f0>{number = 3, name = (null)}
2015-11-16 19:21:07.130 pthread[1841:163595] 三个图片下载完毕:<NSThread: 0x7fe341505770>{number = 1, name = main}
----------------------------------------华丽的分割线----------------------------------------
GCD 一次性任务:dispatch_once_t
一次性任务在整个程序生命周期中只运行一次仅一次.
- (void)viewDidLoad {
[super viewDidLoad];
for (int i = 0; i < 3; i++) {
NSLog(@"进入for循环,执行次数:%d",i);
//创建一个静态的一次性任务对象
static dispatch_once_t onceToken;
//执行一次性任务
dispatch_once(&onceToken, ^{
//只运行一次仅一次
NSLog(@"====只运行一次====");
});
}
}
执行结果:
2015-11-16 19:30:33.530 Demo04-GCD-Group[1894:169586] 进入for循环,执行次数:0
2015-11-16 19:30:33.530 Demo04-GCD-Group[1894:169586] ====只运行一次====
2015-11-16 19:30:33.530 Demo04-GCD-Group[1894:169586] 进入for循环,执行次数:1
2015-11-16 19:30:33.531 Demo04-GCD-Group[1894:169586] 进入for循环,执行次数:2
可以看出来只有一次...
利用一次性任务实现线程安全的单例.
下面用对比的方法来看安全与不安全的区别:
首先创建一个类DataCenter,对这个类设计单例方法,如下:
.h:
#import <Foundation/Foundation.h>
@interface DataCenter : NSObject
//API(Application Program Interface)
//线程不安全创建单例方法
+ (id)sharedDataCenterByUnsafe;
//线程安全创建单例方法
+ (id)sharedDataCenterBySafe;
//initialize方式创建创建单例对象(线程安全->了解)
+ (id)sharedDataCenterByInitialize;
.m:
#import "DataCenter.h"
@implementation DataCenter
//线程不安全
static DataCenter *_dataCenter = nil;
+ (id)sharedDataCenterByUnsafe {
if (_dataCenter == nil) {
//确保两个线程同时执行到实例方法
[NSThread sleepForTimeInterval:1];
_dataCenter = [[TRDataCenter alloc] init];
}
return _dataCenter;
}
//线程安全
+ (id)sharedDataCenterBySafe {
//1.声明一个空的静态的单例对象
static DataCenter *sharedDataCenter = nil;
//2.声明一个静态的gcd单次任务对象
static dispatch_once_t onceToken;
//3.执行gcd的单次任务,对对象进行初始化
dispatch_once(&onceToken, ^{
//初始化
//确保两个线程同时执行到实例方法
[NSThread sleepForTimeInterval:1];
sharedDataCenter = [[DataCenter alloc] init];
});
return sharedDataCenter;
}
//线程安全,听说第三方大多使用这种
static DataCenter *_dataCenterInit = nil;
//重写initialize方法
+ (void)initialize {
if (self == [DataCenter class]) {
//初始化操作
_dataCenterInit = [[DataCenter alloc] init];
}
}
+ (id)sharedDataCenterByInitialize {
return _dataCenterInit;
}
下面调用这些方法:
- (void)viewDidLoad {
[super viewDidLoad];
//为了方便我就直接在这方法里调用了
[NSThread sleepForTimeInterval:1];
[self unsafeSingleton:nil];
[NSThread sleepForTimeInterval:3];
[self safeSingleton:nil];
}
//需求: 验证两个对象的地址是否一样
- (IBAction)unsafeSingleton:(id)sender {
//创建两个线程(NSThread)
NSThread *firstThread = [[NSThread alloc] initWithTarget:self selector:@selector(getUnsafeSingleton) object:nil];
NSThread *secondThread = [[NSThread alloc] initWithTarget:self selector:@selector(getUnsafeSingleton) object:nil];
//启动线程
[firstThread start];
[secondThread start];
}
- (void)getUnsafeSingleton {
DataCenter *dataCenter = [DataCenter sharedDataCenterByUnsafe];
NSLog(@"线程不安全的单例对象地址:%p", dataCenter);
}
- (IBAction)safeSingleton:(id)sender {
//创建两个线程对象
NSThread *firstThread = [[NSThread alloc] initWithTarget:self selector:@selector(getSafeSingleton) object:nil];
NSThread *secondThread = [[NSThread alloc] initWithTarget:self selector:@selector(getSafeSingleton) object:nil];
//启动
[firstThread start];
[secondThread start];
}
- (void)getSafeSingleton {
DataCenter *dataCenter = [DataCenter sharedDataCenterBySafe];
NSLog(@"线程安全的单例对象地址:%p", dataCenter);
}
执行结果:
2015-11-16 19:38:03.930 Demo01-Singleton[1948:174311] 线程不安全的单例对象地址:0x7fa862431e10
2015-11-16 19:38:03.930 Demo01-Singleton[1948:174312] 线程不安全的单例对象地址:0x7fa8625375a0
2015-11-16 19:38:06.931 Demo01-Singleton[1948:174316] 线程安全的单例对象地址:0x7fa86253aff0
2015-11-16 19:38:06.931 Demo01-Singleton[1948:174315] 线程安全的单例对象地址:0x7fa86253aff0
----------------------------------------华丽的分割线----------------------------------------
GCD 延迟执行:
直接上代码了:
- (void)viewDidLoad {
[super viewDidLoad];
[self delayTaskByPerform:nil];
[self delayTaskByGCD:nil];
}
- (IBAction)delayTaskByPerform:(id)sender {
//调用perform延迟方法(5s)
[self performSelector:@selector(excuteTask) withObject:nil afterDelay:5];
//实现延迟的任务逻辑
NSLog(@"执行完毕");
}
- (void)excuteTask {
//主线程
for (int i = 0; i < 10; i++) {
//阻塞主线程
[NSThread sleepForTimeInterval:1];
NSLog(@"++++++%@", [NSThread currentThread]);
}
}
- (IBAction)delayTaskByGCD:(id)sender {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//主线程执行
NSLog(@"延迟任务");
});
NSLog(@"延迟任务执行完毕");
}
执行结果:
2015-11-16 19:51:02.422 Demo05-GCD-Once[2011:180834] 执行完毕
2015-11-16 19:51:02.423 Demo05-GCD-Once[2011:180834] 延迟任务执行完毕
2015-11-16 19:51:08.430 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:09.435 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:10.438 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:11.443 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:12.445 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:13.447 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:14.450 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:15.451 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:16.452 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:17.454 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:17.454 Demo05-GCD-Once[2011:180834] 延迟任务
总结:
第一种是[self performSelector: withObject: afterDelay:];
第二种是dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//主线程执行
//code
});
---------------------------------华丽的分割线---------------------------------
GCD 基本上的内容就差不多了.
从上面代码中也发现了回到主线程的方法了吧!!!
在子线程中使用
dispatch_async(dispatch_get_mainqueue(),^{
//code
});
讲了"老大",接下来讲"老二了"...