1.创建定时器
消息循环是在一个指定的模式下进行的,设置的输入时间也需要指定一个模式,消息循环的模式必须和输入事件的模式匹配才会执行
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建定时器
NSTimer *timer=[NSTimer timerWithTimeInterval:1 target:self selector:@selector(demo) userInfo:nil repeats:YES];
//把定时器添加到消息循环中
//消息循环是在一个指定的模式下进行的,设置的输入时间也需要指定一个模式,消息循环的模式必须和输入事件的模式匹配才会执行
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
-(void)demo{
//消息循环是在一个指定的模式下进行的,设置的输入时间也需要指定一个模式,消息循环的模式必须和输入事件的模式匹配才会执行
//输入当前消息循环的模式,与定时器模式匹配,因此不会中断
NSLog(@"1秒执行一次,%@",[NSRunLoop currentRunLoop].currentMode);
}
@end
2.执行在子线程上的方法及消息循环
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//开启一个子线程
NSThread* thread=[[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
//启动线程
[thread start];
//往子线程消息循环上添加输入源
[self performSelector:@selector(demo2) onThread:thread withObject:nil waitUntilDone:NO];
}
//执行在子线程上的方法
-(void)demo{
NSLog(@"子线程在跑");
//开启子线程的消息循环,因为消息循环上添加了输入源
[[NSRunLoop currentRunLoop]run];
NSLog(@"end");//如果消息循环不退出,则这行无法打印出来
}
//执行在消息循环上
-(void)demo2{
NSLog(@"执行在消息循环上");
}
@end
3.线程池的实现原理
线程池的线程开启后永不销毁,当需要让子线程执行新的方法,使用performselector让指定的方法在指定的子线程上运行
以下案例每次点击,都启用可能不同的线程来处理block块内的代码
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// //gcd是纯C语言的,其函数通常用dispatch开头
// //1.创建队列
// dispatch_queue_t queue=dispatch_get_global_queue(0, 0);
// //2.创建任务
// dispatch_block_t task=^{
// NSLog(@"hello %@",[NSThread currentThread]);
// };
//
// //3.异步执行
// dispatch_async(queue, task);
//融合写法(异步执行)
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"hello %@",[NSThread currentThread]);
});
}
@end
2020-08-05 10:29:44.703539+0800 IOS108[1426:58481] hello <NSThread: 0x60000160ae40>{number = 7, name = (null)}
2020-08-05 10:29:45.123903+0800 IOS108[1426:58480] hello <NSThread: 0x600001650540>{number = 6, name = (null)}
4.使用异步,利用线程池下载网络图片
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)UIScrollView* scrollView;
@property(nonatomic,strong)UIImageView* imageView;
@end
@implementation ViewController
-(void)loadView{
self.scrollView=[[UIScrollView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.scrollView.backgroundColor=[UIColor grayColor];
self.view=self.scrollView;
self.imageView =[[UIImageView alloc]init];
[self.scrollView addSubview:self.imageView];
}
- (void)viewDidLoad {
[super viewDidLoad];
//开启异步执行,下载网络图片
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL* url=[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1596606209250&di=2ba969b702c67b09a9ae5cef36a75a21&imgtype=0&src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F2%2F56a0307306340.jpg"];
NSData* data=[NSData dataWithContentsOfURL:url];
UIImage* image=[UIImage imageWithData:data];
//回到主线程同步更新UI,块内代码是更新主线程的操作
dispatch_sync(dispatch_get_main_queue(), ^{
self.imageView.image=image;
[self.imageView sizeToFit];
self.scrollView.contentSize=image.size;
});
});
}
@end
5.串行队列(任务顺序执行)
//串行队列,同步执行,不开新线程,任务按顺序执行
//串行队列,异步执行,开启新线程,任务按顺序执行
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)UIScrollView* scrollView;
@property(nonatomic,strong)UIImageView* imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//[self demo1];
[self demo2];
}
//串行队列,同步执行,不开新线程,任务按顺序执行
-(void)demo1{
dispatch_queue_t queue=dispatch_queue_create("FR", DISPATCH_QUEUE_SERIAL);
for (int i=0; i<10; i++) {
dispatch_sync(queue, ^{
NSLog(@"hello %d %@",i,[NSThread currentThread]);
});
}
}
//串行队列,异步执行,开启新线程,任务按顺序执行
-(void)demo2{
dispatch_queue_t queue=dispatch_queue_create("FR", DISPATCH_QUEUE_SERIAL);
for (int i=0; i<10; i++) {
dispatch_async(queue, ^{
NSLog(@"hello %d %@",i,[NSThread currentThread]);
});
}
}
@end
6.GCD的两个核心
任务:执行什么操作
队列:用来存放任务(CCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则,先进先出,后进后出)
7.GCD队列的类型
并发队列
- 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
- 并发功能只有在异步(dispatch_async)函数下才有效
串行队列
-
让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
**总结:**
同步和异步决定了要不要开启新的线程
- 同步:在当前线程中执行任务,不具备开启新线程的能力
- 异步:在新的线程中执行任务,具备开启新线程的能力
并发和串行决定了任务的执行方式
- 并发:多个任务并发(同时)执行
- 串行:一个任务执行完毕后,再执行下一个任务
8.并行队列
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)UIScrollView* scrollView;
@property(nonatomic,strong)UIImageView* imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//[self demo1];
[self demo2];
}
//并行队列,同步执行---与串行队列,同步执行一致,不开线程,顺序执行
-(void)demo1{
dispatch_queue_t queue=dispatch_queue_create("FR", DISPATCH_QUEUE_CONCURRENT);
for (int i=0; i<10; i++) {
dispatch_sync(queue, ^{
NSLog(@"hello %d %@",i,[NSThread currentThread]);
});
}
}
//并行队列,异步执行,开多个线程,但线程可重用
-(void)demo2{
dispatch_queue_t queue=dispatch_queue_create("FR", DISPATCH_QUEUE_CONCURRENT);
for (int i=0; i<10; i++) {
dispatch_async(queue, ^{
NSLog(@"hello %d %@",i,[NSThread currentThread]);
});
}
}
@end
打印
2020-08-05 11:39:55.802332+0800 IOS108[1855:95836] hello 1 <NSThread: 0x600002211c40>{number = 6, name = (null)}
2020-08-05 11:39:55.802344+0800 IOS108[1855:95835] hello 2 <NSThread: 0x600002254cc0>{number = 4, name = (null)}
2020-08-05 11:39:55.802367+0800 IOS108[1855:95837] hello 3 <NSThread: 0x60000221a180>{number = 3, name = (null)}
2020-08-05 11:39:55.802389+0800 IOS108[1855:95834] hello 5 <NSThread: 0x60000221a980>{number = 5, name = (null)}
2020-08-05 11:39:55.802397+0800 IOS108[1855:95878] hello 4 <NSThread: 0x600002227d80>{number = 8, name = (null)}
2020-08-05 11:39:55.802410+0800 IOS108[1855:95833] hello 0 <NSThread: 0x60000222b740>{number = 7, name = (null)}
2020-08-05 11:39:55.802472+0800 IOS108[1855:95835] hello 7 <NSThread: 0x600002254cc0>{number = 4, name = (null)}
2020-08-05 11:39:55.802473+0800 IOS108[1855:95836] hello 6 <NSThread: 0x600002211c40>{number = 6, name = (null)}
9.主队列的异步和同步执行
//主队列,异步执行 主线程,顺序执行
//先执行主线程上的代码,才会执行主队列中的任务
/主队列,同步执行 —主线程上执行会产生死锁
//同步执行:会等着第一个任务执行完成,才会继续往后执行
主队列,异步任务
- 不开线程,同步执行
- 住队列特点:如果主线程正在执行代码,暂时不调度任务,等主线程执行结束后再执行任务
- 住队列又叫全局串行对队列
主队列,同步任务
- 程序执行不出来(死锁)
死锁的原因,当程序执行到下面这段代码的时候
-
主队列:如果主线程正在执行代码,就不调度任务
-
同步执行:如果第一个任务没有执行,就继续等待第一个任务执行完毕,在习性下一个任务,此时互相等待,程序无法往下执行(死锁)
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)UIScrollView* scrollView;
@property(nonatomic,strong)UIImageView* imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//[self demo1];
[self demo3];
}
//主队列,异步执行 主线程,顺序执行
//先执行主线程上的代码,才会执行主队列中的任务
-(void)demo1{
for (int i=0; i<10; i++) {
dispatch_async(dispatch_get_main_queue(), ^{//获取主队列,执行异步
NSLog(@"hello %d %@",i,[NSThread currentThread]);
});
}
}
//主队列,同步执行 ---主线程上执行会产生死锁
//同步执行:会等着第一个任务执行完成,才会继续往后执行
-(void)demo2{
NSLog(@"begin");
for (int i=0; i<10; i++) {
dispatch_sync(dispatch_get_main_queue(), ^{//获取主队列,同步执行
NSLog(@"hello %d %@",i,[NSThread currentThread]);
});
}
NSLog(@"end");
}
//解决死锁问题
-(void)demo3{
NSLog(@"begin");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i=0; i<10; i++) {
dispatch_sync(dispatch_get_main_queue(), ^{//获取主队列,同步执行
NSLog(@"hello %d %@",i,[NSThread currentThread]);
});
}
});
NSLog(@"end");
}
@end
打印
2020-08-05 14:58:52.097678+0800 IOS108[2197:157254] begin
2020-08-05 14:58:52.097781+0800 IOS108[2197:157254] end
2020-08-05 14:58:52.098325+0800 IOS108[2197:157254] hello 0 <NSThread: 0x6000001d8d80>{number = 1, name = main}
2020-08-05 14:58:52.098513+0800 IOS108[2197:157254] hello 1 <NSThread: 0x6000001d8d80>{number = 1, name = main}
2020-08-05 14:58:52.098675+0800 IOS108[2197:157254] hello 2 <NSThread: 0x6000001d8d80>{number = 1, name = main}
2020-08-05 14:58:52.098764+0800 IOS108[2197:157254] hello 3 <NSThread: 0x6000001d8d80>{number = 1, name = main}
2020-08-05 14:58:52.098855+0800 IOS108[2197:157254] hello 4 <NSThread: 0x6000001d8d80>{number = 1, name = main}
2020-08-05 14:58:52.098947+0800 IOS108[2197:157254] hello 5 <NSThread: 0x6000001d8d80>{number = 1, name = main}
2020-08-05 14:58:52.099024+0800 IOS108[2197:157254] hello 6 <NSThread: 0x6000001d8d80>{number = 1, name = main}
主队列和串行队列的区别
- 串行队列:必须等待一个任务执行完成,再调度另一个任务
- 主队列:以先进先出调度任务,如果主线程上有代码在执行,住队列不会调度任务
(主队列,同步执行)放入异步执行,解决死锁
10.全局队列
全局队列本质就是并发队列
dispatch_get_gloabl_queue(0,0);
全局队列和并发队列的区别
- 并发队列有名称,可以跟踪错误,全局队列没有
- 在ARC中不需要考虑释放内存,dispatch_release(q)不允许调用。在mrc中需要手动释放内存,并发队列是create创建出来的。在mrc中见到create就要release,全局队列不需要realease
- 一般使用全局队列
11.同步任务
同步任务的作用
- 有些事情是有先后顺序的
- 在网络开发中,通常会把很多任务放在后台异步执行,有些任务会彼此“依赖"
同步任务的特点
- 队列调度多个异步任务前,指定一个同步任务,让所有的异步任务都等待同步任务完成,这就是所谓的"依赖关系"
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//都是耗时操作,要开一个新的线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"输入密码 %@",[NSThread currentThread]);
});
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"扣费 %@",[NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"下载应用%@",[NSThread currentThread]);
});
});
}
@end
2020-08-05 15:46:53.120325+0800 IOS108[2394:176835] 输入密码 <NSThread: 0x6000002b0280>{number = 5, name = (null)}
2020-08-05 15:46:53.120451+0800 IOS108[2394:176835] 扣费 <NSThread: 0x6000002b0280>{number = 5, name = (null)}
2020-08-05 15:46:53.120569+0800 IOS108[2394:176835] 下载应用<NSThread: 0x6000002b0280>{number = 5, name = (null)}
12.各种队列的执行效果
13.Barrier阻塞
主要用于在多个异步操作完成之后,同意对非线程安全的对象进行更新
适合大规模的I/O操作
当方位数据库或文件的时候,更新数据的时候不能和其他更新或读取的操作在同一时间执行,可以使用调度组,不过有点复杂,可以使用dispatch_barrier_async解决
以下代码不能加载到正确的path路径
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)NSMutableArray* photoList;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
for (int i=1; i<=10; i++) {
[self downloadImage:i];
}
}
//懒加载数据
//重写photolist属性
-(NSMutableArray*)photoList{
if (!_photoList) {
self.photoList =[NSMutableArray array];
}
return _photoList;
}
//模拟网络下载多张图片,并将其添加到mutableArray当中,使用异步方式下载
-(void)downloadImage:(int)index{
//异步执行的队列
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//获取文件名字
NSString* filename=[NSString stringWithFormat:@"%02d.jpg",index];
NSLog(@"%@",filename);
NSString* path=[[NSBundle mainBundle] pathForResource:filename ofType:nil];//获取不到正确的路径,显示nil
NSLog(@"%@",path);
UIImage* image=[UIImage imageWithContentsOfFile:path];
//等待队列中所有的任务完成,才会执行barrier中的代码
dispatch_barrier_async(dispatch_get_global_queue(0, 0), ^{
//添加到数组中去
[self.photoList addObject:image];
NSLog(@"图片加载到数组中了 %@ %@",filename,[NSThread currentThread]);
});
});
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"%zd",self.photoList.count);
}
@end
14.CGD的其他操作–延迟操作
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1 延迟执行
//第1个参数:dispatch_time_t when 延迟多长时间 精度到纳秒
//第2个参数:dispatch_queue_t queue 队列
//第3个参数:dispatch_block_t block 任务
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{//延迟了10秒钟
NSLog(@"task");
});
for (int i=0; i<10; i++) {
//2 一次性执行
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{//循环了10次,但是依然是一次执行
NSLog(@"hello %@",[NSThread currentThread]);
});
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//2 一次性执行
//在当前线程上执行,并不拘泥于主线程
static dispatch_once_t onceToken;//一次性执行的原理,判断静态的全局变量的值,默认是0,执行完成后则设置为-1,
//once内部会判断变量的值,如果是0 才执行
dispatch_once(&onceToken,^{//点击多次,但依然是一次执行或者叫执行一次
NSLog(@"hello FR %@",[NSThread currentThread]);
});
});
}
@end
2020-08-05 17:46:26.060146+0800 IOS110[3264:249926] hello <NSThread: 0x600003e34d40>{number = 1, name = main}
2020-08-05 17:46:26.909524+0800 IOS110[3264:250095] hello FR <NSThread: 0x600003e3d2c0>{number = 7, name = (null)}
2020-08-05 17:46:27.088141+0800 IOS110[3264:249926] task
15.单例
先创建单例类NetWorkTools
NetWorkTools.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NetworkTools : NSObject
//设计一个单例
+(instancetype)sharedNetworkTools;
//设计一个用once实现的单例方法
+(instancetype)sharedNetworkToolsOnce;
@end
NS_ASSUME_NONNULL_END
NetWorkTools.m
#import "NetworkTools.h"
@implementation NetworkTools
+(instancetype)sharedNetworkTools{
//由于instance是静态的,不能再次赋值,所以第一次赋值后不能在生成新的对象,因此是单例的
static id instance=nil;
//单例是线程不安全的,加锁保证线程安全
@synchronized (self) {
if (instance==nil) {
instance=[[self alloc]init];
}
}
return instance;
}
//设计一个用once实现的单例方法
+(instancetype)sharedNetworkToolsOnce{
//由于instance是静态的,不能再次赋值,所以第一次赋值后不能在生成新的对象,因此是单例的
static id instance=nil;
static dispatch_once_t onceToken;
//dispatch_once本身就是线程安全的
dispatch_once(&onceToken, ^{
if (instance==nil) {
instance=[[self alloc]init];
}
});
return instance;
}
@end
ViewController.m
#import "ViewController.h"
#import "NetworkTools.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// NetworkTools* tool1=[NetworkTools sharedNetworkTools];
// NetworkTools* tool2=[NetworkTools sharedNetworkToolsOnce];
//
// NSLog(@"%@",tool1);
// NSLog(@"%@",tool2);
[self demo1];
[self demo2];
}
//获取加锁创建对象的时间
-(void)demo1{
CFAbsoluteTime start=CFAbsoluteTimeGetCurrent();
for (int i=0; i<900000; i++) {
[NetworkTools sharedNetworkTools];
}
CFAbsoluteTime end=CFAbsoluteTimeGetCurrent();
NSLog(@"加锁用的时间:%f",end-start);
}
//获取once创建对象的时间
-(void)demo2{
CFAbsoluteTime start=CFAbsoluteTimeGetCurrent();
for (int i=0; i<1000; i++) {
[NetworkTools sharedNetworkToolsOnce];
}
CFAbsoluteTime end=CFAbsoluteTimeGetCurrent();
NSLog(@"once用的时间:%f",end-start);
}
@end
打印
2020-08-05 18:23:00.656033+0800 IOS110[3496:271126] 加锁用的时间:0.060876
2020-08-05 18:23:00.656198+0800 IOS110[3496:271126] once用的时间:0.000030
16.调度组
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//[self demo2];
}
//下载三首歌,当三首歌都下载完毕,通知用户
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//[self demo2];
[self demo1];
}
//调度组的基本使用
-(void)demo1{
//创建组
dispatch_group_t group=dispatch_group_create();
//队列
dispatch_queue_t queue=dispatch_get_global_queue(0, 0);
//下载第一首歌
//参数1,是调度组,参数2,是队列,参数3,是任务
dispatch_group_async(group, queue, ^{
NSLog(@"正在下载第一首歌曲");
});
//下载第二首歌
//参数1,是调度组,参数2,是队列,参数3,是任务
dispatch_group_async(group, queue, ^{
NSLog(@"正在下载第二首歌曲");
});
//下载第三首歌
//参数1,是调度组,参数2,是队列,参数3,是任务
dispatch_group_async(group, queue, ^{
NSLog(@"正在下载第三首歌曲");
});
//提示用户,
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"over");
});
}
打印
2020-08-05 23:03:57.854120+0800 IOS111[4930:379909] 正在下载第一首歌曲
2020-08-05 23:03:57.854141+0800 IOS111[4930:380031] 正在下载第二首歌曲
2020-08-05 23:03:57.854155+0800 IOS111[4930:380032] 正在下载第三首歌曲
2020-08-05 23:03:57.854359+0800 IOS111[4930:379838] over
2020-08-05 23:03:58.807560+0800 IOS111[4930:380031] 正在下载第一首歌曲
2020-08-05 23:03:58.807583+0800 IOS111[4930:380032] 正在下载第三首歌曲
2020-08-05 23:03:58.807585+0800 IOS111[4930:379909] 正在下载第二首歌曲
2020-08-05 23:03:58.807918+0800 IOS111[4930:379838] over
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//[self demo2];
}
//下载三首歌,当三首歌都下载完毕,通知用户
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//[self demo2];
[self demo2];
}
//调度组的基本使用
-(void)demo1{
//创建组
dispatch_group_t group=dispatch_group_create();
//队列
dispatch_queue_t queue=dispatch_get_global_queue(0, 0);
//下载第一首歌
//参数1,是调度组,参数2,是队列,参数3,是任务
dispatch_group_async(group, queue, ^{
NSLog(@"正在下载第一首歌曲");
});
//下载第二首歌
//参数1,是调度组,参数2,是队列,参数3,是任务
dispatch_group_async(group, queue, ^{
NSLog(@"正在下载第二首歌曲");
});
//下载第三首歌
//参数1,是调度组,参数2,是队列,参数3,是任务
dispatch_group_async(group, queue, ^{
NSLog(@"正在下载第三首歌曲");
});
//提示用户,
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"over");
});
}
//调度组的基本原理
-(void)demo2{
//创建组
dispatch_group_t group=dispatch_group_create();
//队列
dispatch_queue_t queue=dispatch_queue_create("FR", DISPATCH_QUEUE_CONCURRENT);
//进入一个组
//执行一个任务
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"任务1");
});
//执行一个任务
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"任务2");
});
//执行一个任务
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"任务3");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"over");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"hello");
//释放组资源
}
@end
17.NSOperation
NSOperation是一个抽象类
- 不能直接使用(方法没有实现)
- 约束子类都具有共同的属性和方法
NSOperation的子类
- NSInvocationOperation
- NSBlockOperation
- 自定义operation
NSOperationQueue队列
18.NSOperation–NSInvocationOperation
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建NSInvocationOperation对象
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//创建NSInvocationOperation对象
NSInvocationOperation* op=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(demo) object:nil];
//创建队列
NSOperationQueue* queue=[[NSOperationQueue alloc]init];
//把操作添加到队列中
[queue addOperation:op];
NSString *str=[NSMutableString string];
}
-(void)demo{
NSLog(@"hello %@",[NSThread currentThread]);
}
@end
打印
2020-08-05 23:35:14.554160+0800 IOS111[5080:397181] hello <NSThread: 0x600000b9e800>{number = 7, name = (null)}
2020-08-05 23:35:15.078358+0800 IOS111[5080:397168] hello <NSThread: 0x600000bca740>{number = 8, name = (null)}
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建NSInvocationOperation对象
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//创建NSInvocationOperation对象
NSInvocationOperation* op=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(demo) object:nil];
[op start];//不开启新线程
[op start];//不开启新线程
}
-(void)demo{
NSLog(@"hello %@",[NSThread currentThread]);
}
@end
打印
2020-08-05 23:39:48.586399+0800 IOS111[5111:400450] hello <NSThread: 0x600002094f00>{number = 1, name = main}
2020-08-05 23:39:49.825822+0800 IOS111[5111:400450] hello <NSThread: 0x600002094f00>{number = 1, name = main}
19.NSOperation–NSBlockOperation
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建NSInvocationOperation对象
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//创建NSBlockOperation对象
NSBlockOperation* op=[NSBlockOperation blockOperationWithBlock:^{
[self demo1];
}];
//更新op的状态,执行main方法
[op start];//不开启新线程
}
-(void)demo1{
NSLog(@"hello %@",[NSThread currentThread]);
}
@end
打印
2020-08-05 23:48:11.270627+0800 IOS111[5180:405627] hello <NSThread: 0x600002ce8d80>{number = 1, name = main}
2020-08-05 23:49:39.859110+0800 IOS111[5180:405627] hello <NSThread: 0x600002ce8d80>{number = 1, name = main}
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建NSInvocationOperation对象
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self demo2];
}
-(void)demo1{
NSLog(@"hello %@",[NSThread currentThread]);
}
-(void)demo2{
//创建操作
NSBlockOperation* op=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"hello %@",[NSThread currentThread]);
}];
//创建队列
NSOperationQueue* queue=[[NSOperationQueue alloc]init];
//把操作添加到队列中
[queue addOperation:op];
}
@end
最简洁形式
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建NSInvocationOperation对象
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self demo3];
}
-(void)demo1{
NSLog(@"hello %@",[NSThread currentThread]);
}
-(void)demo3{
//创建队列
NSOperationQueue* queue=[[NSOperationQueue alloc]init];
//把操作添加到队列中
[queue addOperationWithBlock:^{
NSLog(@"hello %@",[NSThread currentThread]);
}];
}
@end
打印
2020-08-05 23:57:34.751966+0800 IOS111[5295:412766] hello <NSThread: 0x6000025e5ec0>{number = 3, name = (null)}
2020-08-05 23:57:36.312701+0800 IOS111[5295:412766] hello <NSThread: 0x6000025e5ec0>{number = 3, name = (null)}
使用全局队列
#import "ViewController.h"
@interface ViewController ()
//创建全局队列属性
@property(nonatomic,strong)NSOperationQueue* queue;
@end
@implementation ViewController
//懒加载
-(NSOperationQueue*)queue{
if (_queue==nil) {
_queue=[[NSOperationQueue alloc]init];
}
return _queue;
}
- (void)viewDidLoad {
[super viewDidLoad];
//创建NSInvocationOperation对象
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self demo3];
}
//使用全局队列
-(void)demo3{
//把操作添加到队列中
[self.queue addOperationWithBlock:^{
NSLog(@"hello %@",[NSThread currentThread]);
}];
}
@end
打印:
2020-08-06 00:04:37.444267+0800 IOS111[5370:417981] hello <NSThread: 0x6000010eee00>{number = 7, name = (null)}
2020-08-06 00:04:39.063206+0800 IOS111[5370:417981] hello <NSThread: 0x6000010eee00>{number = 7, name = (null)}
19.线程间通信
子线程回到主线程上去更新UI
主队列
- 添加主队列的操作,最终都执行在主线程上
- [NSOperationQueue mainQueue]
当前队列
- 获取当前操作所在的队列
- [NSOperationQueue currentQueue]
模拟图片下载完成后回到主线程更新UI
#import "ViewController.h"
@interface ViewController ()
//创建一个全局队列
@property(nonatomic,strong)NSOperationQueue* queue;
@end
@implementation ViewController
//懒加载
-(NSOperationQueue*)queue{
if (!_queue) {
_queue=[[NSOperationQueue alloc]init];
}
return _queue;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.queue addOperationWithBlock:^{
//异步下载图片
NSLog(@"异步下载图片");
//线程间通信,回到主线程更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"更新UI");
}];
}];
}
@end
2020-08-06 00:23:10.793339+0800 IOS112[5515:429200] 异步下载图片
2020-08-06 00:23:10.804259+0800 IOS112[5515:429045] 更新UI
20.大总结
//1 消息循环
//主线程的消息循环默认开启,子线程的消息循环默认不开启
//2 消息循环的目的
//保证程序不退出
//负责处理输入事件
//3 输入事件和消息循环的模式
//输入事件的模式必须跟当前消息循环的模式匹配,输入事件才会执行
//4 子线程消息循环 run
//5 GCD
//队列 串行队列 并行队列 全局(并行)队列 主队列
//任务
//执行 同步执行 异步执行
//6 各种组合
//6.1 串行队列
//同步执行 不开线程,顺序执行
//异步执行 开一个线程,顺序执行
//6.2 并行队列(全局队列)
//同步执行 不开线程,顺序执行
//异步执行 开多个线程,无序执行
//6.3 主队列
//同步执行 在主线程上执行的话会死锁
//异步执行 主线程,顺序执行
//7 Barrier 阻塞 多线程中操作线程不安全的类,要小心
//8 after 延迟执行
//9 once 一次性执行 线程安全的
//单例 保证一个程序中只有一份单例对象(工具类)
//10 调度组 让多个异步任务 都完成之后做一件事情
//11 NSOperation 抽象类 抽象类中的方法都没有实现,等待子类去实现(约束子类)
//NSInvocationOperation
//NSBlockOperation
//start 不开线程 会设置操作的状态 会调用main
//操作添加到队列 -- 并行队列,异步执行 开多个线程,无序执行
//12 NSOperationQueue