1. 程式人生 > >iOS多執行緒學習之NSOperation(一)

iOS多執行緒學習之NSOperation(一)

 什麼是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];

}

通過列印結果我們可以看到上面任務僅在兩條執行緒中執行(是指同一時間開的執行任務的執行緒數)