什么是NSOperation呢?有什么用呢?和GCD相比有什么不同呢?或者优势呢?

NSOperation底层实现是基于GCD的,比GCD多了一些简单使用的功能,使用更加面向对象

作用:配合使用NSOperation 和 NSOperationQueue也能实现多线程

NSOperation 和 NSOperationQueue实现多线程的步骤:
(1)将需要的任务先封装到一个NSOperation对象中(GCD封装在block中)

(2)然后将NSOperation添加到NSOperationQueue对象中(GCD将block添加到队列中)

(3)系统自动将NSOperationQueue中的NSOperation取出来

(4)将取出的NSOperation封装的操作放到一条新线程中执行

在使用之前还要说下:NSOperation是一个抽象类,所谓抽象类就是没什么功能的类,并不具备封装操作的能力,抽象么,具体不出来,能有啥用,只能靠想象了,但这是实际操作写代码啊,不是想想啊,所以我们就要用到它的子类了,换句话说,他的子类不抽象,可以实现封装操作

使用NSOperation子类的三种方式:
(一)NSInvocationOperation

(二)NSBlockOperation

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

下面意义列举

(一)NSInvocationOperation(这个功能基本很少用,为什么呢,下面看看就知道了)

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self invocationQueue];
    
}

- (void)invocationQueue {

    NSInvocationOperation * invoQueue = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
    [invoQueue start];
}

- (void)download {
    
    NSLog(@"load----%@",[NSThread currentThread]);
}

@end



通过打印发现,这个操作在主线程中运行的,也就是调用start方法没有开辟线程,直接变成同步(在当前线程)执行了,写了这么一大堆,然并卵,还不如直接调用[self downLoad]呢,那么下面我们想另开线程应该怎么做呢?犹如上面的步骤说明,把任务添加到NSOperationQueue对象中:

- (void)invocationQueue {

    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    
    NSInvocationOperation * invoQueue = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
    //[invoQueue start];
    [queue addOperation:invoQueue];
}

- (void)download {
    
    NSLog(@"load----%@",[NSThread currentThread]);
}

@end



通过打印发现不是num!=1,不是主线程,添加到队列中,会自动异步执行

即使这样也感觉比较垃圾,还不如直接用NSThread呢,下面看看另一种方法

- (void)viewDidLoad {
    [super viewDidLoad];

    //[self invocationQueue];
    [self blockQueue];
    
}

- (void)blockQueue {
    
    NSBlockOperation * bkOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage1---%@", [NSThread currentThread]);
    }];
    [bkOperation start];
}



通过上面可以知道调用start是同步执行,打印出来的结果是num=1,在主线程,但是也有一点不同,在添加几个任务,再看看打印结果

- (void)viewDidLoad {
    [super viewDidLoad];

    //[self invocationQueue];
    [self blockQueue];
    
}

- (void)blockQueue {
    
    NSBlockOperation * bkOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage1---%@", [NSThread currentThread]);
    }];
    
    [bkOperation addExecutionBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    [bkOperation addExecutionBlock:^{
        NSLog(@"loadImage3---%@", [NSThread currentThread]);
    }];
    [bkOperation start];
    
}



打印结果多了两个线程,由此可以得知,当通过start执行任务数大于1时会开启异步线程,但是1还是在主线程,这个也很少用,另外就是面试业主要问队列,下面我们就用队列:

- (void)blockQueue {
    
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];

    NSBlockOperation * bkOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage1---%@", [NSThread currentThread]);
    }];
    
    [bkOperation addExecutionBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    [bkOperation addExecutionBlock:^{
        NSLog(@"loadImage3---%@", [NSThread currentThread]);
    }];
    
    [queue addOperation:bkOperation];
    //[bkOperation start];
    
}



通过打印结果就可以知道我们将任务放到队列中后就会异步执行了,也可以这样写:


- (void)blockQueue {
    
    NSBlockOperation * bkOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage11---%@", [NSThread currentThread]);
    }];
    
    [bkOperation addExecutionBlock:^{
        NSLog(@"loadImage12---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * bkOperation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * bkOperation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage3---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * bkOperation4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage4---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * bkOperation5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage4---%@", [NSThread currentThread]);
    }];
    
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];

    [queue addOperation:bkOperation];
    [queue addOperation:bkOperation1];
    [queue addOperation:bkOperation3];
    [queue addOperation:bkOperation4];
    [queue addOperation:bkOperation5];
    //[bkOperation start];
    
}

(三)NSOperationQueue

- (void)viewDidLoad {
    [super viewDidLoad];

    //[self invocationQueue];
   // [self blockQueue];
    [self opeationQueue];
}

- (void)opeationQueue {

    //创建一个队列(非主队列)
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    
    //添加操作到队列中(自动并发执行,和GCD的Global Dispatch Queue相似)
    NSBlockOperation * blockQueue1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * blockQueue2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    [queue addOperation:blockQueue1];
    [queue addOperation:blockQueue2];
    
}



解释:先把block里面的操作包装成operation对象,在把这个对象添加到队列中,block中的任务就是在异步线程中执行,其实这样写还是有点麻烦,我们可以通过queue一步写到位:

- (void)opeationQueue {

    //创建一个队列(非主队列)
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    
    //添加操作到队列中(自动并发执行,和GCD的Global Dispatch Queue相似)
    NSBlockOperation * blockQueue1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage1---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * blockQueue2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    [queue addOperation:blockQueue1];
    [queue addOperation:blockQueue2];
    [queue addOperationWithBlock:^{
       NSLog(@"loadImage3---%@", [NSThread currentThread]);
    }];
}



通过打印我们就发现三个线程,说白点,就是有三个operation对象,只不过最后一个block内部帮我们封装了,而前两个是我们自己创建的

所以我们搞个任务想异步执行,可以直接用最后一个方式了。

- (void)viewDidLoad {
    [super viewDidLoad];

    //[self invocationQueue];
   // [self blockQueue];
    [self opeationQueue];
}

- (void)opeationQueue {

    //创建一个队列(非主队列)
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    
    //添加操作到队列中(自动并发执行,和GCD的Global Dispatch Queue相似)
//    NSBlockOperation * blockQueue1 = [NSBlockOperation blockOperationWithBlock:^{
//        NSLog(@"loadImage1---%@", [NSThread currentThread]);
//    }];
//    NSBlockOperation * blockQueue2 = [NSBlockOperation blockOperationWithBlock:^{
//        NSLog(@"loadImage2---%@", [NSThread currentThread]);
//    }];
//    [queue addOperation:blockQueue1];
//    [queue addOperation:blockQueue2];
    [queue addOperationWithBlock:^{
       NSLog(@"loadImage3---%@", [NSThread currentThread]);
    }];
}



上面是基本用法:

添加操作到队列中有两种方法:

- (void)addOperation:(NSOperation *)op;

- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

(点进NSOperationQueue官方文档也可以看到这两个方法)


那么高级一点的用法又长啥样啊?怎么用?先看一下下面执行的结果

- (void)opeationQueue {

    //创建一个队列(非主队列)
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    
    //添加操作到队列中(自动并发执行,和GCD的Global Dispatch Queue相似)
    NSBlockOperation * blockQueue1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage1---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * blockQueue2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * blockQueue3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * blockQueue4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * blockQueue5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    [queue addOperation:blockQueue1];
    [queue addOperation:blockQueue2];
    [queue addOperation:blockQueue3];
    [queue addOperation:blockQueue4];
    [queue addOperation:blockQueue5];

}



打印发现,我靠,多少个任务多少条线程啊,假如我们几百个任务开了几百条线程,不用我说,你也知道占用内存太多了,太浪费资源了,而GCD内部会帮我们自动管理线程条数,最多也就4、5条,但是这是系统决定的,我们操作不了,那么我们NSOperationQueue可不可以控制线程个数来提高性能呢?答案是可以,下面我们可以设置一下最大并发数:

- (void)opeationQueue {

    //1.创建一个队列(非主队列)
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    
    //2.设置最大并发数
    queue.maxConcurrentOperationCount = 2;
    
    //3.添加操作到队列中(自动并发执行,和GCD的Global Dispatch Queue相似)
    NSBlockOperation * blockQueue1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage1---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * blockQueue2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * blockQueue3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * blockQueue4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    NSBlockOperation * blockQueue5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"loadImage2---%@", [NSThread currentThread]);
    }];
    [queue addOperation:blockQueue1];
    [queue addOperation:blockQueue2];
    [queue addOperation:blockQueue3];
    [queue addOperation:blockQueue4];
    [queue addOperation:blockQueue5];

}



通过打印结果我们可以看到上面任务仅在两条线程中执行(是指同一时间开的执行任务的线程数)