多线程(三)之NSOperation

//与GCD使用的步骤一样,并且也不需要管理线程,可调度线程池负责线程的创建回收优化.
    //1.创建操作
    //NSOperation是抽象类,不能直接使用,只有定义没有实现,因为是抽象类,使用子类或者系统已经定义好的2个子类
    //(NSInvocationOperation or NSBlockOperation)

1.定义:

1.小结:定义:一旦提到NSOperation就要想到,这个类是个抽象类,什么是抽象类,就是我们不会具体去用他,而是去用他的子类去实例化对象

NSOperation有两个子类:NSInvocationOperation和NSBlockOperation

首先是NSInvocationOperation的用法:

- (void)demo1 {
    //与GCD使用的步骤一样,并且也不需要管理线程,可调度线程池负责线程的创建回收优化.
    //1.创建操作
    //NSOperation是抽象类,不能直接使用,只有定义没有实现,因为是抽象类,使用子类或者系统已经定义好的2个子类
    //(NSInvocationOperation or NSBlockOperation)
    /*
     NSInvocationOperation 年代比较久远,一般不用
     1.对象
     2.方法
     3.参数
     */
    NSInvocationOperation *innvocationOp = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task) object:nil];
    
    //Begins the execution of the operation.在当前线程运行
//    [innvocationOp start];
    
    //2.队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //3.把操作+到队列中
    [queue addOperation:innvocationOp];
}

NSBlockOperation的用法一:基本用法:

和GCD 一样,只有将操作加到队列中去执行才能开线程,否则还是在主线程中执行

-(void)demo2{
//NSBlockOperation
    
    //参数是一个block,回调
    NSBlockOperation  *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"task2 is running %@",[NSThread currentThread]);
    }];
    
//    [blockOp start];
    //2.队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //3.把操作+到队列中
    [queue addOperation:blockOp];
    
}

NSBlockOperation的用法二:常用方法,即基本用法的简化版本:直接把操作加入到队列中默认是并发队列异步执行

-(void)demo4{
//开发中常用写法
    //直接把一个blockOperation+到队列
    //操作无序,多个线程->并发队列,异步执行!!!!
    for (int i = 0; i<20; i++) {
        [self.queue addOperationWithBlock:^{
            NSLog(@"task2 is running %@ %d",[NSThread currentThread],i);
        }];
    }
}

2.最大并发数

最大并发数定义:

什么是最大并发数?

上图是我模拟线程处理操作的流程图:

即操作添加到队列中,队列会从可调度线程池申请线程,可调度线程池就会开一条线程,队列调度操作在此线程上执行,如果设置了最大操作并发数,就是指可调度线程池最多能开的线程数.

体现在代码上假设我设置的最大并发数是2,那么就是最多只能有两个操作同时异步并发操作,也就是会两个两个的打印,如下所示:看打印结果的时间

//
//  ViewController.m
//  最大并发数
//
//  Created by apple on 16/8/22.
//  Copyright © 2016年 itcast. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@property(nonatomic,strong) NSOperationQueue *queue;

@end

@implementation ViewController

-(NSOperationQueue *)queue{
    if(!_queue){
        _queue = [[NSOperationQueue alloc] init];
        //设置最大并发数:同一时间,只能够有哦固定数量的子线程去执行队列中的操作
        _queue.maxConcurrentOperationCount = 2;
    }
    return _queue;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self demo2];
}

-(void)demo2{

    //开启了至少60+线程
    for (int i = 0; i<99; i++) {
        [self.queue addOperationWithBlock:^{
            [NSThread sleepForTimeInterval:1];
            NSLog(@"%d %@",i,[NSThread currentThread]);
        }];
    }
    
}

//错误代码
- (void)demo1 {
    //使用GCD,异步并发,很多任务->线程开的太多,资源损耗,GCD不能法规定开启的线程数量
    for (int i = 0; i<999999; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });
    }
}

@end
2016-12-13 17:16:36.995 最大并发数[10417:1467862] 0 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:36.995 最大并发数[10417:1467863] 1 <NSThread: 0x600000267180>{number = 4, name = (null)}
2016-12-13 17:16:37.996 最大并发数[10417:1467862] 2 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:37.996 最大并发数[10417:1467863] 3 <NSThread: 0x600000267180>{number = 4, name = (null)}
2016-12-13 17:16:39.070 最大并发数[10417:1467862] 4 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:39.070 最大并发数[10417:1467863] 5 <NSThread: 0x600000267180>{number = 4, name = (null)}
2016-12-13 17:16:40.140 最大并发数[10417:1467865] 7 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:40.140 最大并发数[10417:1467862] 6 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:41.205 最大并发数[10417:1467871] 8 <NSThread: 0x60000007b9c0>{number = 6, name = (null)}
2016-12-13 17:16:41.204 最大并发数[10417:1467865] 9 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:42.205 最大并发数[10417:1467871] 11 <NSThread: 0x60000007b9c0>{number = 6, name = (null)}
2016-12-13 17:16:42.205 最大并发数[10417:1467862] 10 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:43.274 最大并发数[10417:1467862] 13 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:43.274 最大并发数[10417:1467871] 12 <NSThread: 0x60000007b9c0>{number = 6, name = (null)}
2016-12-13 17:16:44.348 最大并发数[10417:1467862] 14 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:44.348 最大并发数[10417:1467871] 15 <NSThread: 0x60000007b9c0>{number = 6, name = (null)}
2016-12-13 17:16:45.423 最大并发数[10417:1467871] 17 <NSThread: 0x60000007b9c0>{number = 6, name = (null)}
2016-12-13 17:16:45.423 最大并发数[10417:1467862] 16 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:46.491 最大并发数[10417:1467871] 18 <NSThread: 0x60000007b9c0>{number = 6, name = (null)}
2016-12-13 17:16:46.491 最大并发数[10417:1467862] 19 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:47.491 最大并发数[10417:1467862] 20 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:47.492 最大并发数[10417:1467871] 21 <NSThread: 0x60000007b9c0>{number = 6, name = (null)}
2016-12-13 17:16:48.493 最大并发数[10417:1467862] 22 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:48.493 最大并发数[10417:1467865] 23 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:49.494 最大并发数[10417:1467862] 24 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:49.494 最大并发数[10417:1467865] 25 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:50.560 最大并发数[10417:1467865] 27 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:50.560 最大并发数[10417:1467862] 26 <NSThread: 0x600000264d40>{number = 3, name = (null)}
2016-12-13 17:16:51.628 最大并发数[10417:1467865] 29 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:51.628 最大并发数[10417:1467871] 28 <NSThread: 0x60000007b9c0>{number = 6, name = (null)}
2016-12-13 17:16:52.696 最大并发数[10417:1467867] 30 <NSThread: 0x608000075d40>{number = 7, name = (null)}
2016-12-13 17:16:52.696 最大并发数[10417:1467865] 31 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:53.771 最大并发数[10417:1467867] 32 <NSThread: 0x608000075d40>{number = 7, name = (null)}
2016-12-13 17:16:53.771 最大并发数[10417:1467865] 33 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:54.837 最大并发数[10417:1467865] 35 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:54.837 最大并发数[10417:1467867] 34 <NSThread: 0x608000075d40>{number = 7, name = (null)}
2016-12-13 17:16:55.910 最大并发数[10417:1467865] 36 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:55.910 最大并发数[10417:1467867] 37 <NSThread: 0x608000075d40>{number = 7, name = (null)}
2016-12-13 17:16:56.972 最大并发数[10417:1467865] 38 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:56.972 最大并发数[10417:1467867] 39 <NSThread: 0x608000075d40>{number = 7, name = (null)}
2016-12-13 17:16:58.047 最大并发数[10417:1467867] 41 <NSThread: 0x608000075d40>{number = 7, name = (null)}
2016-12-13 17:16:58.047 最大并发数[10417:1467865] 40 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:59.112 最大并发数[10417:1467865] 43 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:16:59.112 最大并发数[10417:1467867] 42 <NSThread: 0x608000075d40>{number = 7, name = (null)}
2016-12-13 17:17:00.182 最大并发数[10417:1467865] 44 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:17:00.182 最大并发数[10417:1467867] 45 <NSThread: 0x608000075d40>{number = 7, name = (null)}
2016-12-13 17:17:01.253 最大并发数[10417:1467865] 46 <NSThread: 0x60800007e380>{number = 5, name = (null)}
2016-12-13 17:17:01.253 最大并发数[10417:1467871] 47 <NSThread: 0x60000007b9c0>{number = 6, name = (null)}

小结:通过代码demo1也说明了GCD不具备管理线程的能力,这也是NSOperation比GCD多的一个优点

3.开启,暂停,继续,取消

//
//  ViewController.m
//  暂停,取消,继续
//
//  Created by apple on 16/8/22.
//  Copyright © 2016年 itcast. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@property(nonatomic,strong) NSOperationQueue *queue;

@end

@implementation ViewController
//开启->暂停->取消->开启 不运行
-(NSOperationQueue *)queue{
    if(!_queue){
    
        _queue = [[NSOperationQueue alloc] init];
    //最大并发数
        _queue.maxConcurrentOperationCount = 2;
    }
    return _queue;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}


- (IBAction)beginClick:(id)sender {
    NSLog(@"开启");
    for (int i = 0; i<99; i++) {
        [self.queue addOperationWithBlock:^{
            [NSThread sleepForTimeInterval:1];
            NSLog(@"%d %@",i,[NSThread currentThread]);
        }];
    }
//for循环这种,记住都是把操作全部添加到队列中,再执行的 }
- (IBAction)pauseClick:(id)sender { NSLog(@"暂停"); //暂停的是队列中还没有执行的操作,已经执行的操作不能被打断,暂停 [self.queue setSuspended:YES]; } - (IBAction)resumeClick:(id)sender { NSLog(@"继续"); [self.queue setSuspended:NO]; } - (IBAction)cancelClick:(id)sender { NSLog(@"取消"); //取消的是队列中还没有执行的操作,已经执行的操作不能取消 //队列已经被挂起,在往队列中假如操作不能运行,需要setSuspended:NO [self.queue cancelAllOperations]; } @end

4.模拟tiger的思路

先进行界面布局,点击开始按钮,把任务添加到队列,while死循环执行随机数,判断是否是开启状态,如果是,就在主线程添加给控件赋值随机数的操作

这里是用if判断来进行开始和暂停

 

//
//  ViewController.m
////
//  Created by apple on 16/8/22.
//  Copyright © 2016年 itcast. All rights reserved.
//

#import "ViewController.h"

/*
 1.搭建界面
 2.写一个方法,让数字动起来
 3.子线程运行randomDispaly
 4.UI更新->主线程
 */
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label1;

@property (weak, nonatomic) IBOutlet UILabel *label2;
@property (weak, nonatomic) IBOutlet UILabel *label3;
@property (weak, nonatomic) IBOutlet UIButton *btn;

@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, typically from a nib.
}

- (IBAction)btnClick:(id)sender {
    
    //1.判断是否开启了操作,当前队列中只有一个操作,并且正在执行,不能通过setSuspended停止操作,通过操作数量来判断是否已经执行
    //2.停止正在执行的操作
    if(self.queue.operationCount == 0){
        //开启操作
        [self.queue addOperationWithBlock:^{
            [self randomDispaly];
        }];
        
        
        [self.btn setTitle:@"暂停" forState:UIControlStateNormal];
        
        [self.queue setSuspended:NO];
        
        
    }else{
       //setSuspended 方法本身不能够停止一个正在执行的操作,只是改变了属性的值,通过值的判断,在while条件判断,让循环退出
        
         [self.btn setTitle:@"开始" forState:UIControlStateNormal];
        [self.queue setSuspended:YES];
    }
    
}

//随机显示数字到label上,模拟tiger机轮动起来的效果
//当while循环的时候,成为一个`耗时任务`,要在子线程运行
- (void)randomDispaly {
//    NSLog(@"%@",[NSThread currentThread]);
    //[0~10)
    while (![self.queue isSuspended]) {
        
        //变得慢一点
        //不写这句话,暂停一时半会儿停不了 i/o
        //由于屏幕是IO设备,速度很慢,即使在内存CPU已经把队列暂停了,但是他还在处理之前没有显示的任务
        [NSThread sleepForTimeInterval:0.1];

        int randomNumber1 = arc4random_uniform(10);
        int randomNumber2 = arc4random_uniform(10);
        int randomNumber3 = arc4random_uniform(10);
        
        
        //回到主线程去运行,线程间通信
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.label1.text = [NSString stringWithFormat:@"%d",randomNumber1];
            self.label2.text = [NSString stringWithFormat:@"%d",randomNumber2];
            self.label3.text = [NSString stringWithFormat:@"%d",randomNumber3];
        }];
        
        
    }
}

@end

5.优先级:优先级高的不一定比优先级低的先执行,只是有更多的可能性被CPU 调用

 优先级分为操作优先级和队列优先级:

操作优先级:提高某一个操作的优先级,看先执行哪个操作

- (void)demo1 {

    for (int i = 0; i<20; i++) {
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        }];
        
        //提高某一个操作的优先级
        if(i == 10){
            //被废弃
//            [op setQueuePriority:NSOperationQueuePriorityVeryHigh];
            //IOS8
            [op setQualityOfService:NSQualityOfServiceUserInitiated];
        }
        
        [self.queue addOperation:op];
    }

}

队列优先级:在队列初始化的时候设置队列的优先级,看打印结果

//优先级:高的不一定比低的先运行,有更多的可能性被CPU调用
-(NSOperationQueue *)queue{
    if(!_queue){
        _queue = [[NSOperationQueue alloc] init];
        //设置队列的优先级
        [_queue setQualityOfService:NSQualityOfServiceBackground];
    }
    return _queue;
}

-(NSOperationQueue *)queue1{
    if(!_queue1){
        _queue1 = [[NSOperationQueue alloc] init];
         [_queue1 setQualityOfService:NSQualityOfServiceUserInteractive];
    }
    return _queue1;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self demo2];
}

//队列的优先级
-(void)demo2{
    for (int i = 0; i<20; i++) {
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"----> %d %@",i,[NSThread currentThread]);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@">>>>> %d %@",i,[NSThread currentThread]);
        }];
        
        [self.queue addOperation:op];
        
        [self.queue1 addOperation:op2];
    }
}

6.设置依赖关系

由于NSOperation是默认异步并发执行操作,不像GCD可以设置同步执行,按顺序一步步来,那么有没有办法可以做到NSOperation操作同步执行呢?

下面就是NSOperation的同步方法:设置依赖

//
//  ViewController.m
//  下载音乐Demo
//
//  Created by apple on 16/8/22.
//  Copyright © 2016年 itcast. All rights reserved.
//

#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, typically from a nib.
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self demo1];
}

- (void)demo1 {
    /*
     1.登陆
     2.付款
     3.下载
     4.UI更新
     1,2,3,4有序->GCD同步的方式
     1,2,3耗时任务->子线程
     4->主线程
     */
    
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1.登陆%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2.付款%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3.下载%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"4.UI更新%@",[NSThread currentThread]);
    }];
    
    //设置依赖关系.可以跨队列设置,一定要在操作+到队列之间设置
    [op2 addDependency:op1];
    [op3 addDependency:op2];
    [op4 addDependency:op3];
    //避免头尾相接的循环依赖,死锁
//    [op1 addDependency:op4];
    
    [self.queue addOperations:@[op1,op2,op3] waitUntilFinished:NO];
    //主队列
    [[NSOperationQueue mainQueue] addOperation:op4];
    
    
    
}

@end

重点来了:NSOperation的综合练习:图片的异步下载思路:

 

posted @ 2016-12-15 21:25  忆缘晨风  阅读(228)  评论(0编辑  收藏  举报