4.NSOperation
- **NSOperation的兩個核心概念 : **操作和隊列
- **介紹 : **
1.NSOperation是一個抽象類,并不具備封裝操作的能力,必須使用它的子類
NSOperation的子類
1.NSInvocationOperation
2.NSBlockOperation
3.自定義繼承NSOperation的子類,實現(xiàn)子類內(nèi)部的main(對象方法)方法
2.只用配合使用NSOperation和NSOperationQueue才能實現(xiàn)多線程編程
通過NSOperation和NSOperationQueue實現(xiàn)多線程的具體步驟:
1.將需要的操作封裝到一個NSOperation對象中
2.將NSOperation對象添加到NSOperationQueue隊列對象中
3.系統(tǒng)會自動將NSOperationQueue隊列對象中的NSOperation對象取出
4.將取出的NSOperation對象封裝的操作方法,放到一條新線程中執(zhí)行
操作 - 封裝操作的幾種方式
**方式一 : **NSInvocationOperation
作用:封裝操作,默認是不會開啟線程的,只會在當前的線程中執(zhí)行操作
NSInvocationOperation * iop1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
[iop1 start];//開啟任務
//注意:這里沒有將操作添加到隊列中,是不會開心新的線程去執(zhí)行操作的,只會在當前線程中同步執(zhí)行操作
**方式二 : **NSBlockOperation
作用:封裝操作,默認是不會開啟線程的,只有在一個操作內(nèi)有多個任務或者將操作添加到隊列中,才會開啟線程
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"1----%@",[NSThread currentThread]);
}];
//2.開啟任務
[op1 start];
//追加任務(會自動開子線程,任務的執(zhí)行時并發(fā)執(zhí)行的)
[op3 addExecutionBlock:^{//追加的操作有可能在子線程中執(zhí)行,也有主線程中執(zhí)行
NSLog(@"4----%@",[NSThread currentThread]);
}];
**注意 : **
1.追加任務,會自動開啟子線程,并且任務的執(zhí)行是并發(fā)執(zhí)行的(追加的任務不一定是在子線程中執(zhí)行,也有可能是在主線程中執(zhí)行)
2.當操作中的任務數(shù)量大于1時,就會開啟子線程(其實就是和1中說的一樣)
**方式三 : **自定義NSOperation
封裝操作:重寫main方法,將所有的的操作都放在main方法里面,說明start方法也是調(diào)用main方法的
在自定義的NSOperation類的@implementation中的main方法
//在子類內(nèi)重寫main方法實現(xiàn)任務
-(void)main
{
NSLog(@"-----customClas-----%@",[NSThread currentThread]);
}
在外界的調(diào)用
//默認不會開線程,只會在當前線程內(nèi)執(zhí)行任務
ZHJOperation * customOp = [[ZHJOperation alloc] init];
//注意:自定義的NSOperation也需要調(diào)用start方法才能執(zhí)行,但是僅僅是這樣,是不會開啟子線程的,只有將操作添加到隊列中,才會開啟子線程
[customOp start];
隊列 NSOperationQueue
- NSOperation的隊列和GCD中的隊列相似,只不過NSOperation中的隊列分為主隊列和非主隊列
- 主隊列
**特點 : **
1)所有的任務都是在主隊列中執(zhí)行的
2)主隊列是一個串行的隊列
**獲取隊列以及添加操作到隊列 : **
- 主隊列
//創(chuàng)建隊列-主隊列
NSOperationQueue * queue = [NSOperationQueue mainQueue];
//將操作添加到隊列中
[queue addOperation:operation];
- 非主隊列
**特點 : ** 同時具備串行和并發(fā)的功能,默認情況下是并發(fā)的,可以手動設(shè)置為串行隊列,通過設(shè)置最大并發(fā)數(shù)屬性來更改.
**獲取隊列以及添加操作到隊列 : **
//獲取隊列 - 非主隊列
NSOperationQueue*queue = [[NSOperationQueue alloc]init];
//將操作添加到隊列中
[queue addOperation:op1];
**注意 : **將操作添加到隊列中的方法"addOperation:",已經(jīng)在內(nèi)部執(zhí)行了start方法了,所以在將操作添加到隊列后,不再需要執(zhí)行start方法來執(zhí)行操作了
**
由于NSInvocationOperation/NSBlockOperation/自定義的NSOperation將操作添加到隊列的方法是一樣的,同上面介紹主隊列或非主隊列時的將操作添加到隊列中樣,所以就不再重復編寫
**
**
簡便方法(僅限于NSBlockOperation類) : **也可以說是快速將操作加入到隊列的方法,這個方法不需要創(chuàng)建操作
//通過addOperationWithBlock:添加的操作,會比通過NSBlockOperation創(chuàng)建的操作先執(zhí)行
[queue addOperationWithBlock:^{
NSLog(@"4-----------%@",[NSThread currentThread]);
}];
-
最大并發(fā)數(shù)
**作用 : **同一時間內(nèi),最多可以處理的任務的數(shù)量
@property NSInteger maxConcurrentOperationCount;
注意:
1.隊列默認是并發(fā)隊列
2.“-1”在計算機中,標識的是最大值(即默認是不受限制的),這里同樣表示最大值, 最大并發(fā)數(shù), 具體多少根據(jù)系統(tǒng) CPU 的使用率, 分配 并發(fā)數(shù)
3.在操作添加到隊列之前
4.一般最多設(shè)置為6;
5.將最大操作數(shù)的值設(shè)置為1,可以實現(xiàn)任務的串行效果,但是要注意的是,并不是只開一條子線程(通常會開兩條子線程,循環(huán)回收復用)
6.如果設(shè)置為0,表示 任何任務都不會執(zhí)行
7.如果一組任務是按順序執(zhí)行的,就是串行隊
使用方法
//表示同一時間內(nèi)最多執(zhí)行2各任務,并且是并發(fā)執(zhí)行
queue.maxConcurrentOperationCount=2;
- NSOperation常用的方法
-
隊列的掛起(也稱暫停)
作用:暫停隊列中的任務
任務的狀態(tài):
隊列中的任務是有狀態(tài)的(如執(zhí)行狀態(tài),和就緒狀態(tài)),而且當前正在處于執(zhí)行狀態(tài)的任務是不能夠暫停的,只能暫停就緒的任務
暫停
-
隊列的掛起(也稱暫停)
示例:
-(IBAction)pauseBtn:(id)sender {
//暫停當前隊列中的操作,停止執(zhí)行
[self.queue setSuspended:YES];
}
恢復(暫停的逆設(shè)置)
-(IBAction)resumBtn:(id)sender {
//回復當前隊列中的操作,繼續(xù)執(zhí)行
[self.queue setSuspended:NO];
}
注意:
1.并不能立刻暫停當前執(zhí)行的任務
2.當前正在執(zhí)行的任務是不能暫停的,暫停的是等待的任務
- 隊列的取消
-(IBAction)cancellBtn:(id)sender {
[self.queue cancelAllOperations];
}
注意
0.暫停和取消操作不能作用于當前正在執(zhí)行的任務,只能作用在隊列中就緒執(zhí)行的任務
1.取消是取消隊列中的所有任務,除了正在執(zhí)行的任務
2.一旦被取消,就不能回復之前的操作
3.調(diào)用了cancelAllOperations方法的話,cancelled屬性的值也會跟隨改變,由no變?yōu)閥es 或又yes改變?yōu)閚o
- ****自定義****Operation****類的掛起取消****
注意:當自定義一個操作,點擊取消后,任務不會結(jié)束
如何實現(xiàn)自定義Operation類的掛起取消:
在自定義的main方法內(nèi)判斷calcelled屬性的真假,因為當外界執(zhí)行了cancelAllOperations方法后,cancelled屬性的值就會發(fā)生改變,變?yōu)閥es
-(void)main
{
for (int i = 0; i <1000; i++) {
NSLog(@"1-------%d--------%@",i,[NSThread currentThread]);
}
for (int i = 0; i <1000; i++) {
NSLog(@"2-------%d--------%@",i,[NSThread currentThread]);
}
if (self.cancelled) {//判斷外界是否執(zhí)行了cancelAllOperations操作
return;
}
如何快速結(jié)束任務:
可以將判斷放到耗時操作內(nèi)(上邊的程序就是指for循環(huán)內(nèi)),但不推薦這么做,因為判斷操作也非常耗費性能
**蘋果官方給的建議 : **
自定義的NSOperation可以在每一段耗時操作之后都去判斷一下cancel屬性是否為真
-
操作的依賴和監(jiān)聽
-
操作依賴:
基本介紹:想要讓一個操作在另一個操作執(zhí)行完畢的時候再執(zhí)行,可以使用操作依賴來完成
使用方法:addDependencing
-
操作依賴:
//設(shè)置依賴,也就是設(shè)置操作的執(zhí)行順序
[op1 addDependency:op2]; // op1 依賴 op2
[op2 addDependency:op3]; // op2 依賴 op3
[op3 addDependency:op4]; // op3 依賴 op4
**注意點 : **
1.操作依賴必須在添加到隊列之前來進行設(shè)置
2.操作依賴可以跨隊列依賴,很強大
操作依賴與死鎖:
如果設(shè)置了循環(huán)依賴會發(fā)生死鎖,也就是說與循環(huán)依賴有關(guān)系的所有操作都不會執(zhí)行
//!!!!不能設(shè)置循環(huán)依賴(死鎖--op1&op2 不會執(zhí)行)
[op2 addDependency:op1];
[op1 addDependency:op2];
- **操作監(jiān)聽 : **(一個操作結(jié)束之后給我們發(fā)送一條消息)
使用方法:op1.completionBlock{},也就是說當op1結(jié)束之后會自動調(diào)用block中的代碼塊
//completionBlock是一個屬性,既可以通過點語法設(shè)置,也可以通過set方法設(shè)置
op1.completionBlock = ^ {
NSLog(@"op1已經(jīng)執(zhí)行完畢");
};
[op1 setCompletionBlock:^{
NSLog(@"通過setCompletionBlock方法實現(xiàn)對op1的監(jiān)聽");
}];