iOS開發多線程篇—NSOperation簡單介紹
一、NSOperation簡介
1. 簡單說明
a. NSOperation的作用:
是OC語言中基於GCD的面向對象的封裝,使用起來比GCD更加簡單(面向對象);
提供了一些用GCD不好實現的功能,使用NSOperation不用關心線程以及線程的生命週期(蘋果推薦使用)。
b. 配合使用NSOperation和NSOperationQueue實現多線程編程的具體步驟:
(1)先將需要執行的操作封裝到一個NSOperation對象中
(2)然後將NSOperation對象添加到NSOperationQueue中
(3)系統會⾃動將NSOperationQueue中的NSOperation取出來
(4)將取出的NSOperation封裝的操作放到⼀條新線程中執⾏
c. NSOperation的子類
NSOperation是一個抽象類,不能直接使用(方法沒有實現),約束子類都具有共同的屬性和方法,並不具備封裝操作的能力,必須使⽤它的子類。
使用NSOperation⼦類的方式有3種:
(1)NSInvocationOperation
(2)NSBlockOperation
(3)自定義子類繼承NSOperation,實現內部相應的⽅法
2. 具體說明
2.1 NSInvocationOperation子類【不常用】
創建對象和執行操作:
//創建操作對象,封裝要執行的任務
//NSInvocationOperation 封裝操作
NSInvocationOperation *operation=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test)
object:nil];
//執行操作
[operation start];
注意:操作對象默認在主線程中執行,只有添加到隊列中才會開啓新的線程。即默認情況下,如果操作沒有放到隊列中queue中,都是同步執行。只有將NSOperation放到一個NSOperationQueue中,纔會異步執行操作,不常用!
2.2 NSBlockOperation子類
注意:只要NSBlockOperation封裝的操作數 > 1,就會異步執行操作。
創建對象和添加操作:
//創建NSBlockOperation操作對象
NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"NSBlockOperation1------%@",[NSThread currentThread]);
}];
//通過addExecutionBlock:方法添加更多的操作
[operation addExecutionBlock:^{
NSLog(@"NSBlockOperation2------%@",[NSThread
currentThread]);
}];
[operation start];
打印結果:
2015-10-26 14:06:11.765 test1[7036:326837] NSBlockOperation1------<NSThread: 0x7f8628e074d0>{number = 1, name = main}
2015-10-26 14:06:11.767 test1[7036:326882] NSBlockOperation2------<NSThread: 0x7f8628e2ef10>{number = 2, name = (null)}
2.3 NSOperationQueue
NSOperation可以調⽤start⽅法來執⾏任務,但默認是同步執行的。
如果將NSOperation添加到NSOperationQueue(操作隊列)中,系統會自動異步執行NSOperation中的操作。
也就是說添加操作到NSOperationQueue中,自動執行操作,自動開啓線程。
添加操作到NSOperationQueue中的兩種那個方法:
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
注意:系統自動將NSOperationqueue中的NSOperation對象取出,將其封裝的操作放到一條新的線程中執行,這些任務是並行執行的。
提示:隊列的取出是有順序的,與打印結果並不矛盾。這就好比,選手A,B,C雖然起跑的順序是先A,後B,然後C,但是到達終點的順序卻不一定是A,B在前,C在後。
下面使用for循環打印,可以更明顯的看出任務是併發執行的。
代碼示例:
//創建NSInvocationOperation對象,封裝操作
NSInvocationOperation
*operation1=[[NSInvocationOperation
alloc]initWithTarget:self
selector:@selector(test1)
object:nil];
NSInvocationOperation
*operation2=[[NSInvocationOperation
alloc]initWithTarget:self
selector:@selector(test2)
object:nil];
//創建NSBlockOperation對象,封裝操作
NSBlockOperation
*operation3=[NSBlockOperation
blockOperationWithBlock:^{
for (int
i=0; i<5; i++) {
NSLog(@"NSBlockOperation3--1----%@",[NSThread
currentThread]);
}
}];
[operation3
addExecutionBlock:^{
for (int
i=0; i<5; i++) {
NSLog(@"NSBlockOperation3--2----%@",[NSThread
currentThread]);
}
}];
//創建NSOperationQueue
NSOperationQueue
* queue=[[NSOperationQueue
alloc]init];
//把操作添加到隊列中
[queue
addOperation:operation1];
[queue
addOperation:operation2];
[queue
addOperation:operation3];
}
-(void)test1
{
for (int
i=0; i<5; i++) {
NSLog(@"NSInvocationOperation1--%@",[NSThread
currentThread]);
}
}
-(void)test2
{
for (int
i=0; i<5; i++) {
NSLog(@"NSInvocationOperation2--%@",[NSThread
currentThread]);
}
}
打印結果:
二、NSOperation 和 GCD 的比較
1. GCD
a. GCD是iOS4.0 推出的,主要針對多核cpu做了優化,是C語言的技術;
b. GCD是將任務(block)添加到隊列(串行/並行/全局/主隊列),並且以同步/異步的方式執行任務的函數;
c. GCD提供了一些NSOperation不具備的功能
1)一次性執行
2)延遲執行
3)調度組
2. NSOperation
a. NSOperation是iOS2.0推出的,iOS4之後重寫了NSOperation;
b. NSOperation將操作(異步的任務)添加到隊列(併發隊列),就會執行指定的操作;
c. NSOperation裏提供了方便的操作:
1)最大併發數:同時執行的任務數。
比如,同時開3個線程執行3個任務,併發數就是3。
最大併發數的相關方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
示例:
-(NSOperationQueue
*)queue {
if(!_queue){
_queue
=[[NSOperationQueue
alloc]
init];
//設置對大併發數
_queue.maxConcurrentOperationCount
= 3;
}
return
_queue;
}
2)隊列的暫定/繼續,;
// YES代表暫停隊列,NO代表恢復隊列。
- (void)setSuspended:(BOOL)b;
- (BOOL)isSuspended;
3)取消隊列的所有操作
- (void)cancelAllOperations;
提示:也可以調用NSOperation的- (void)cancel方法取消單個操作。
示例:
//暫停,暫停的是隊列中還沒有被運行的操作,正在運行的操作不能被暫停;
//當隊列爲暫停的時候,往隊列中加入操作,也不會運行。
- (IBAction)pauseClick:(id)sender
{
NSLog(@"點擊暫停");
[self.queue
setSuspended:YES];
}
//繼續
- (IBAction)resumeClick:(id)sender
{
NSLog(@"點擊繼續");
[self.queue
setSuspended:NO];
}
//取消
- (IBAction)cancelClick:(id)sender
{
NSLog(@"點擊取消");
[self.queue
cancelAllOperations];
}
4)指定操作之間的依賴關係(GCD可以用同步實現)
a. NSOperation之間可以設置依賴來保證執行順序,比如一定要讓操作A執行完後,才能執行操作B,可以這麼寫:
// 操作B依賴於操作A
[operationB addDependency:operationA];
b. 可以在不同queue的NSOperation之間創建依賴關係。
c. 注意不能相互依賴,比如A依賴B,B依賴A。
示例:
-(void)demo{
NSBlockOperation
*op1 = [NSBlockOperation
blockOperationWithBlock:^{
//模擬耗時操作
[NSThread
sleepForTimeInterval:arc4random_uniform(3)];
NSLog(@"1.登陸
%@",[NSThread
currentThread]);
}];
NSBlockOperation
*op2 = [NSBlockOperation
blockOperationWithBlock:^{
[NSThread
sleepForTimeInterval:arc4random_uniform(3)];
NSLog(@"2.扣費
%@",[NSThread
currentThread]);
}];
NSBlockOperation
*op3 = [NSBlockOperation
blockOperationWithBlock:^{
[NSThread
sleepForTimeInterval:arc4random_uniform(3)];
NSLog(@"3.下載
%@",[NSThread
currentThread]);
}];
NSBlockOperation
*op4 = [NSBlockOperation
blockOperationWithBlock:^{
NSLog(@"4.UI更新
%@",[NSThread
currentThread]);
}];
//添加操作依賴,要在操作放到隊列之前。
//操作依賴可以跨隊列設置。
[op2
addDependency:op1];
[op3
addDependency:op2];
[op4
addDependency:op3];
[self.queue
addOperations:@[op1,op2,op3]
waitUntilFinished:NO];
[[NSOperationQueue
mainQueue]
addOperation:op4];
}
5)操作隊列的優先級
設置NSOperation在queue中的優先級,可以改變操作的執行優先級。
操作的優先級,優先級高的操作不一定最先運行。
隊列的優先級 ,優先級高的隊列不一定比優先級低的隊列先運行完,而是有更多的可能被執行到。
6)線程間通信:
子線程->主線程
操作方式與GCD類似。
示例:
[self.queue addOperationWithBlock:^{
//子線程:do something
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//主線程的任務,操作UI等
}];
}];
此外:
有些操作任務如果在主線程完成,會嚴重的影響到用戶體驗,造成UI卡的現象。我們可以通過自定義NSOperation,新開線程,讓加載圖片的任務異步執行,來解決此問題。