NSOperation简单使用

配合使用NSOperation 和 NSOperationQueue 实现多线程编程。

NSOperation实现多线程的步骤

1, 将要执行的操作封装到NSOperation对象中
2, 将NSOperation 对象添加到 NSOperationQueue中
3, 系统自动将NSOperationQueue中的NSOperation 取出来
4, 将NSOperation 封装的操作放到一条新线程执行

NSOperation的子类

NSOperation是个抽象类,并不具有封装操作的能力。使用方式有3种
1,NSInvocationOperation
2,NSBlockOperation
3,自定义子类继承NSOperation ,实现内部相应的方法

NSOperationQueue 队列组

1 ,默认就是并发执行的啦
2, 那么我们怎么设置串行执行呢 。队列组有个属性 maxConcurrentOperationCount 设置最大并发数 ,就可以啦,这样就是串行执行的了
maxConcurrentOperationCount = 1 ;串行队列
maxConcurrentOperationCount > 1 ; 并发队列
maxConcurrentOperationCount = 0 ; 不会执行任务
maxConcurrentOperationCount = -1 ; 特殊意义。表示的是最大值。
默认并发数就是 static const NSInteger NSOperationQueueDefaultMaxConcurrentOperationCount = -1;

/*
  暂停可恢复,取消不可恢复
  队列中的任务也是有状态的:已经执行完毕 | 正在执行|排队等待 ,不管是暂停还是取消都会等待正在执行的任务结束才会执行。
*/

//暂停  ,不能暂停当前正在执行的任务
    [queue setSuspended:YES];
// 继续
     [queue setSuspended:NO];
/*取消,不可恢复 。取消队列里面的所有任务,我们能拿到 cancelled 属性,
这样就可以判断队列是否取消任务,可以在 队列任务里面进行判断,
用于即时取消任务等,因为正在执行的任务不会里面执行,
所有cancelled 。可以帮助判断*/
    [queue cancelAllOperations];

NSOperationQueue 的一个只读属性 cancelled 。返回BOOL,队列是否取消了任务

3,注意:串行执行 != 只开了一条线程

方式一 :NSInvocationOperation 简单的封装任务,封装操作,不和NSOperationQueue配合使用的时候,没有任何作用,并不会开启子线程。


-(void)invocationOperation{
    //1,封装操作,封装任务
    /*
     1.参数1:目标对象 self
     2.参数2:调用的方法
     3.参数3:方法的参数
     */
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];
    NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test2) object:nil];

    /*
     [NSOperationQueue mainQueue]; 获取主队列
     NSOperation队列:
     1,主队列 : [NSOperationQueue mainQueue] 和gcd主队列一样,任务都在主线程执行,主队列是串行队列
     2,非主队列:[[NSOperationQueue alloc] init] 特殊(同时具备并发和串行的功能),默认情况下非主队列是并发队列
     */
    
    //2 ,创建队列
   NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //设置最大并发数的数量,同一时间最多多少任务可以执行 ,设置为1 就是串行啦
    queue.maxConcurrentOperationCount = 2;
    //3 , 添加操作到队列中 ,
    [queue addOperation:op1]; //内部已经调用了  [op1 start]; ,调用了start后又调用了内部的main方法 。我们可以用继承重写start,和main 方法。把任务封装到 main方法里面
    [queue addOperation:op2];
    [queue addOperation:op3];
    
    //,启动操作 ,如果不 和NSOperationQueue 配合操作。就必须手动启动一下,
    //并且NSInvocationOperation,单独使用并不会开启多线程
    //[op1 start];
}

-(void)test
{
    NSLog(@"%s __func__  NSThread  = %@" ,__func__,[NSThread currentThread]);
}
-(void)test1
{
    NSLog(@"%s __func__  NSThread  = %@" ,__func__,[NSThread currentThread]);
}
-(void)test2
{
    NSLog(@"%s __func__  NSThread  = %@" ,__func__,[NSThread currentThread]);
}
/* 打印 ,看打印,不难理解,内部是并发执行
2017-11-06 23:08:11.404 NSOperationDemo[1847:56183] -[ViewController test] __func__  NSThread  = {number = 4, name = (null)}
2017-11-06 23:08:11.404 NSOperationDemo[1847:56165] -[ViewController test2] __func__  NSThread  = {number = 5, name = (null)}
2017-11-06 23:08:11.404 NSOperationDemo[1847:56166] -[ViewController test1] __func__  NSThread  = {number = 3, name = (null)}
*/

方式二 :NSBlockOperation 单独使用不配合 queue 队列, 不会开启子线程,但是一个操作中任务数量大于1 ,会开启子线程,详情如下

-(void)blockOperation
{
    //1,创建任务
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3 -- %@",[NSThread currentThread]);
    }];
    
    
    //2 ,追加任务
    //注意: 如果一个操作中任务数量大于1 , 会开子线程并发执行任务,
    并不一定是子线程也许是主线程,比如op3 就有4个任务,所以会开子线程,
    并发执行任务。而 op1 ,op2,不会开子线程在主线程执行
    [op3 addExecutionBlock:^{
        NSLog(@"4 -- %@",[NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"5 -- %@",[NSThread currentThread]);
    }];
   
    [op3 addExecutionBlock:^{
        NSLog(@"6 -- %@",[NSThread currentThread]);
    }];
    
    //3 ,启动
    [op1 start];
    [op2 start];
    [op3 start];
  
}

/* 打印结果
2017-11-06 22:38:20.505 NSOperationDemo[1399:33218] 1 -- {number = 1, name = main}
2017-11-06 22:38:20.505 NSOperationDemo[1399:33218] 2 -- {number = 1, name = main}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33218] 3 -- {number = 1, name = main}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33218] 5 -- {number = 1, name = main}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33259] 4 -- {number = 3, name = (null)}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33261] 6 -- {number = 4, name = (null)}
*/

方式二:NSBlockOperation 单独使用配合 queue 队列,开启多线程

#pragma mark NSBlockOperation 配合队列   queue 的 使用
-(void)blockOperationWithQueue
{
    //1,创建任务
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3 -- %@",[NSThread currentThread]);
    }];
    
    //2 ,追加任务
    //注意: 如果一个操作中任务数量大于1 , 会开子线程并发执行任务,并不一定是子线程也许是主线程,比如op3 就有4个任务,所以会开子线程,并发执行任务
    [op3 addExecutionBlock:^{
        NSLog(@"4 -- %@",[NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"5 -- %@",[NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"6 -- %@",[NSThread currentThread]);
    }];
    
    //2 ,创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //3 ,启动
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    
    //简便方法 ,相当于,创建了任务,并添加到了 queue
    [queue addOperationWithBlock:^{
        NSLog(@"7 -- %@",[NSThread currentThread]);
    }];
}

/* 打印如下
  2017-11-06 23:17:08.596 NSOperationDemo[1886:61154] 7 -- {number = 7, name = (null)}
2017-11-06 23:17:08.596 NSOperationDemo[1886:61099] 1 -- {number = 6, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61155] 2 -- {number = 8, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61154] 3 -- {number = 7, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61113] 4 -- {number = 4, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61099] 5 -- {number = 6, name = (null)}
2017-11-06 23:17:08.599 NSOperationDemo[1886:61155] 6 -- {number = 8, name = (null)
*/

方式三:自定义子类继承NSOperation ,实现内部相应的方法

好处:1,利于代码隐蔽
2, 复用性,耦合性

#import 
//XCOperation 继承子类实现
@interface XCOperation : NSOperation
@end

#import "XCOperation.h"
@implementation XCOperation
//告诉要执行的任务是什么
-(void)main{
    NSLog(@"__Func__ %s , NSThread = %@",__func__,[NSThread currentThread]);
}

#pragma mark 继承 NSOperation  使用方法
-(void)customOperation
{
    //1 ,封装操作
    XCOperation *op1 = [[XCOperation alloc] init];
    XCOperation *op2 = [[XCOperation alloc] init];

    //2 ,创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //3 ,添加 启动
    [queue addOperation:op1];
    [queue addOperation:op2];
}
/* 打印
 NSOperationDemo[1950:69064] __Func__ -[XCOperation main] , NSThread = {number = 3, name = (null)}
 NSOperationDemo[1950:69044] __Func__ -[XCOperation main] , NSThread = {number = 4, name = (null)}
*/

NSBlockOperation 操作依赖,控制任务顺序 ,监听任务完成情况。

#pragma mark NSBlockOperation 操作依赖 ,控制任务顺序 ,于操作监听,监听某个任务完成状态
-(void)blockOperationWithQueueRelyon
{
    //1,创建任务
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 8; i ++) {
            NSLog(@"3 -- %@",[NSThread currentThread]);
            if (i == 7) {
                NSLog(@"任务3 ,打印到了7 跳出循环任务结束");
                break;
            }
        }
    }];
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"4 -- %@",[NSThread currentThread]);
    }];
    
    //2 ,创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];

    //3.操作依赖
    //注意:不能循环依赖,比如操作1 依赖 操作3 。操作3 又来依赖操作1 。会互相等待
    //这里是操作1依赖操作3 。操作3 依赖操作2. 所有打印顺序是  2 -> 3 -> 1 -> 4
    //可以跨队列依赖
    [op1 addDependency:op3]; // 任务1 依赖任务3
    [op3 addDependency:op2]; // 任务3 依赖任务2
    [op4 addDependency:op1]; // 任务4 依赖任务1  所以 打印顺序是  2 -> 3 -> 1 -> 4
    
    //4.添加 到队列组
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue1 addOperation:op4];
    
    //5,操作监听,监听任务3 ,任务3 完成了,打印这里
    op3.completionBlock = ^{
        NSLog(@"监听到任务3结束了 ,虽然监听到任务3结束,但是他和任务3并不一定在一个线程 ,%@",[NSThread currentThread]);
    };
    
}
/* 可以看出顺序是2 3 1 4 ,监听到了任务3
2017-11-07 22:52:18.344 NSOperationDemo[1045:29786] 2 -- {number = 7, name = (null)}
2017-11-07 22:52:18.346 NSOperationDemo[1045:30254] 3 -- {number = 10, name = (null)}
2017-11-07 22:52:18.346 NSOperationDemo[1045:30254] 3 -- {number = 10, name = (null)}
2017-11-07 22:52:18.347 NSOperationDemo[1045:30254] 3 -- {number = 10, name = (null)}
2017-11-07 22:52:18.347 NSOperationDemo[1045:30254] 3 -- {number = 10, name = (null)}
2017-11-07 22:52:18.348 NSOperationDemo[1045:30254] 3 -- {number = 10, name = (null)}
2017-11-07 22:52:18.348 NSOperationDemo[1045:30254] 3 -- {number = 10, name = (null)}
2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 3 -- {number = 10, name = (null)}
2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 3 -- {number = 10, name = (null)}
2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 任务3 ,打印到了90 跳出循环任务结束
2017-11-07 22:52:18.351 NSOperationDemo[1045:29786] 监听到任务3结束了 ,虽然监听到任务3结束,但是他和任务3并不一定在一个线程 ,{number = 7, name = (null)}
2017-11-07 22:52:18.351 NSOperationDemo[1045:30254] 1 -- {number = 10, name = (null)}
2017-11-07 22:52:18.354 NSOperationDemo[1045:29786] 4 -- {number = 7, name = (null)}

*/

线程之间的通信 。例如: 子线程下载图片,下载完成通知主线程刷新

#pragma makr 线程中的通信 ,例子子线程下载图片,主线程刷新

-(void)downloadImage
{
    //1 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    //2 封装任务
    NSBlockOperation *download = [NSBlockOperation blockOperationWithBlock:^{
        NSURL *url = [NSURL URLWithString:@"http://static.firefoxchina.cn/img/201710/4_59e999c8e6aa50.jpg"];
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        NSLog(@"子线程下载图片%@" ,[NSThread currentThread]);
        UIImage *image = [UIImage imageWithData:imageData];
      
        //4,线程通信,回到主线程,刷新UI
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageview.image = image ;
        }];

//      [queue addOperationWithBlock:^{
//           NSLog(@"7 -- %@",[NSThread currentThread]);
//      }];
//        [queue addOperationWithBlock:^{
//            NSLog(@"8 -- %@",[NSThread currentThread]);
//        }];
//        [queue addOperationWithBlock:^{
//            NSLog(@"9 -- %@",[NSThread currentThread]);
//        }];

    }];
    
    //3 添加到queue
    [queue addOperation:download];
}

学习记录,简单的敲一下增强记忆,便于查找,

NSThread简单的入门
GCD入门

你可能感兴趣的:(NSOperation简单使用)