一. 定義
NSOperation
是蘋果公司提供的一套多線程解決方案, 它是基于GCD
的更抽象的"面向?qū)ο?封裝.
二. 對比GCD
- 支持任務(wù)之間添加依賴關(guān)系, 控制執(zhí)行順序
- 提供可選實現(xiàn)的任務(wù)完成
block
- 可使用KVO監(jiān)聽任務(wù)執(zhí)行狀態(tài)變化
- 支持設(shè)置任務(wù)的優(yōu)先級, 可控制任務(wù)的相對執(zhí)行順序
- 提供取消操作
三. 任務(wù)
任務(wù)就是在線程中執(zhí)行的代碼, 在GCD中表現(xiàn)形式是block
, NSOperation 是在其子類NSInvocationOperation, NSBlockOperation, 自定義子類中執(zhí)行任務(wù)代碼.
四. 隊列
NSOperationQueue表示隊列, 用來存放任務(wù)的隊列
- NSOperation單獨使用時是同步操作, 配合NSOperationQueue才能實現(xiàn)異步操作
- GCD添加到隊列中的任務(wù)遵守
FIFO
原則, 對于添加到NSOperationQueue中的任務(wù), 首先根據(jù)任務(wù)之間的依賴關(guān)系決定任務(wù)的就緒狀態(tài), 進(jìn)入就緒狀態(tài)的任務(wù)由任務(wù)之間的相對優(yōu)先級決定執(zhí)行順序 - NSOperationQueue可以設(shè)置最大并發(fā)任務(wù)數(shù)量
- NSOperationQueue 提供兩種隊列: 主隊列和自定義隊列,
- 主隊列在主線程運(yùn)行
- 自定義隊列在后臺執(zhí)行
五. 基本使用
NSOperation
是一個抽象類, 使用的時候必須子類化, 提供三種方式創(chuàng)建任務(wù):
1. 使用子類NSInvocationOperation
NSInvocationOperation *invocationOpeartion = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOpeartion) object:nil];
// 如果沒有添加到 NSOperationQueue, 則需要手動調(diào)用 start
[invocationOpeartion start];
/// 回調(diào)方法
- (void)invocationOpeartion {
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}
注意: NSInvocationOperation單獨使用時, 并沒有開啟新的線程, 任務(wù)都是在當(dāng)前線程中執(zhí)行
2. 使用子類NSBlockOperation
- 2.1 簡單使用
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任務(wù)代碼
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
[blockOperation start];
注意: NSBlockOperation單獨使用時, 并沒有開啟新的線程, 任務(wù)都是在當(dāng)前線程中執(zhí)行
- 2.2: NSBlockOperation類有一個實例方法
addExecutionBlock
, 這個方法可以添加一個代碼塊, 當(dāng)執(zhí)行NSBlockOperation
對象start
時, 該對象將其所有塊提交給默認(rèn)優(yōu)先級的并發(fā)調(diào)度隊列. 然后對象等待所有的塊完成執(zhí)行. 當(dāng)最后一個塊完成執(zhí)行時, 操作對象將自己標(biāo)記為已完成. 由于塊操作本身在單獨的線程上運(yùn)行, 所以應(yīng)用程序的其他線程可以在等待塊操作完成的同時繼續(xù)工作.
需要說明的一點是, 如果添加的任務(wù)較多的話, 這些操作(包括blockOperationWithBlock中的操作)可能在不同的線程中并發(fā)執(zhí)行, 這是由系統(tǒng)決定的
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任務(wù)代碼
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
[blockOperation addExecutionBlock:^{
NSLog(@"addExecutionBlock1:%@", NSThread.currentThread);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"addExecutionBlock2:%@", NSThread.currentThread);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"addExecutionBlock3:%@", NSThread.currentThread);
}];
[blockOperation start];
在調(diào)用了addExecutionBlock方法添加了多組任務(wù)后技俐,開啟新的線程,任務(wù)是并發(fā)執(zhí)行的,blockOperationWithBlock中的任務(wù)執(zhí)行是否在當(dāng)前的線程執(zhí)行, 統(tǒng)一由系統(tǒng)調(diào)度.
3. 自定義繼承自NSOperation
的子類
@interface Operation : NSOperation
@end
@implementation Operation
-(void)main {
if (!self.isCancelled) {
for (int i = 0; i < 4; i++) {
sleep(2);
NSLog(@"%d==%@", i, NSThread.currentThread);
}
}
}
@end
// 使用:
Operation *op = [[Operation alloc] init];
[op start];
注意: 自定義的Operation并沒有開啟新的線程,任務(wù)的執(zhí)行是在當(dāng)前的線程中執(zhí)行的。
六. 隊列: NSOperationQueue
NSOperationQueue
提供了主隊列和自定義隊列
- 主隊列:
[NSOperationQueue mainQueue];
, 凡是添加到主隊列的NSOperation
任務(wù)都會放到主線程執(zhí)行 - 自定義隊列:
[[NSOperationQueue alloc] init]
, 凡是添加到自定義隊列的NSOperation
任務(wù)都會放到子線程執(zhí)行.
1.添加任務(wù)到隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 方式1:
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任務(wù)代碼
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
[queue addOperation:blockOperation];
// 方式2:
[queue addOperationWithBlock:^{
// 任務(wù)代碼
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
// 注意: 不需要調(diào)用NSBlockOperation
的 start
方法
2.添加多個操作到隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 操作1
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任務(wù)代碼
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
// 操作2
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
// 方式1:
[queue addOperation:blockOperation];
[queue addOperation:invocationOperation];
// 方式2:
// waitUntilFinished參數(shù)祖今,如果傳YES,則表示會等待隊列里面的任務(wù)執(zhí)行完成后才會往下執(zhí)行,也就是會阻塞線程
[queue addOperations:@[blockOperation, invocationOperation] waitUntilFinished:true];
/// 回調(diào)方法
- (void)invocationOperation {
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}
總結(jié): waitUntilFinished
參數(shù)击你,如果傳YES
,則表示會等待隊列里面的任務(wù)執(zhí)行完成后才會往下執(zhí)行谎柄,也就是會阻塞線程
七. 同步&并發(fā)
NSOperationQueue
有個一屬性maxConcurrentOperationCount
maxConcurrentOperationCount
默認(rèn)-1, 不做限制, 在子線程中執(zhí)行任務(wù)
maxConcurrentOperationCount
= 1, 隊列中的任務(wù)會同步執(zhí)行, 阻塞當(dāng)前線程
maxConcurrentOperationCount
> 1, 隊列中的任務(wù)會并發(fā)執(zhí)行, 開啟子線程執(zhí)行
maxConcurrentOperationCount
值并不是表示并發(fā)執(zhí)行的線程數(shù)量丁侄,而是在一個隊列中能夠同時執(zhí)行的任務(wù)的數(shù)量。
八. NSOperation線程間的通訊, 子線程完成任務(wù), 會主線程更新UI
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任務(wù)代碼
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
// 回主隊列更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"更新UI");
}];
}];
[queue addOperation:blockOperation];
九. 任務(wù)的依賴關(guān)系
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
// 任務(wù)代碼
NSLog(@"1");
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2");
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3");
}];
// 1依賴于2和3, 意思是在2和3都執(zhí)行完后才會執(zhí)行1;
[operation1 addDependency:operation2];
[operation1 addDependency:operation3];
// 將1的優(yōu)先級設(shè)置最高
operation1.queuePriority = NSOperationQueuePriorityHigh;
[queue addOperations:@[operation1, operation2, operation3] waitUntilFinished:NO];
//log: 2 3 1
總結(jié):
queuePriority
不能取代依賴關(guān)系
queuePriority
屬性只對同一個隊列有效
十.設(shè)置完成回調(diào)
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
// 任務(wù)代碼
NSLog(@"1");
}];
// 設(shè)置任務(wù)1完成的回調(diào)block
[operation1 setCompletionBlock:^{
NSLog(@"任務(wù)1完成了");
}];
十一. 監(jiān)聽狀態(tài)
- 使用KVO觀察對operation狀態(tài)的監(jiān)聽: isExcuting, isFinished, isCancelled.
- isExcuting 正在執(zhí)行任務(wù)
- isFinished 任務(wù)完成
- isCancelled 任務(wù)取消執(zhí)行