IOS开发教程第一季之03多线程day2--定时器,消息循环,线程池、异步、同步、队列、阻塞、延迟、单例、调度组,NSOperation、线程间通信

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值