NSOperation

NSOperation

NSOperation簡介

  • Operation : 操作的意思.
  • 是OC語言中基于GCD的面向?qū)ο蟮姆庋b.
  • 使用起來比GCD更加簡單(面向?qū)ο螅?/li>
  • 提供了一些用GCD不好實(shí)現(xiàn)的功能.
  • 蘋果推薦使用,使用NSOperation不用關(guān)心線程以及線程的生命周期.
GCD和OP的關(guān)系圖.png

NSOperation類的介紹

  1. NSOperation是個(gè)抽象類,無法直接使用.因?yàn)榉椒ㄖ挥新暶鳑]有實(shí)現(xiàn).

    作為父類使用的.約束子類共有的屬性和方法.

  2. 子類 :

    • NSInvocationOperation
    • NSBlockOperation
    • 自定義NSOperation

    操作默認(rèn)是異步的.

  3. 隊(duì)列 : NSOperationQueue

    隊(duì)列默認(rèn)是并發(fā)的.

  4. 核心 :

    • GCD的核心 : 將任務(wù)添加到隊(duì)列
    • OP的核心 : 將操作添加到隊(duì)列

使用步驟

  1. 先將需要執(zhí)行的操作封裝到一個(gè)NSOperation對象中.創(chuàng)建NSOperation對象.
  2. 將NSOperation對象添加到NSOperationQueue中.
  3. NSOperationQueue會(huì)自動(dòng)將NSOperation取出來.
  4. 將取出的NSOperation封裝的操作自動(dòng)放到一條對應(yīng)的新線程中執(zhí)行.
操作添加到隊(duì)列.png

NSInvocationOperation

  • 核心 : 將操作添加到隊(duì)列.

NSInvocationOperation 基本使用演練

- (void)demo:(id)parram
{
    // 查看當(dāng)前線程
    NSLog(@"%@ %@",parram,[NSThread currentThread]);
}

OP調(diào)用start方法

- (void)opDemo1
{
    // 創(chuàng)建操作對象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo:) object:@"InvocationOperation"];
    // 調(diào)用start方法
    [op start];
}
  • [op start]; 方法,會(huì)在當(dāng)前線程執(zhí)行 @selector方法.

NSInvocationOperation 將操作添加到隊(duì)列

- (void)opDemo2
{
    // 操作對象 : OP中的操作對象默認(rèn)是異步執(zhí)行
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo:) object:@"InvocationOperation"];
    // 隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 將操作添加到隊(duì)列
    [queue addOperation:op];
}
  • 操作 : 默認(rèn)是異步執(zhí)行.

NSInvocationOperation 驗(yàn)證隊(duì)列并發(fā)性

- (void)opDemo3
{
    // 隊(duì)列 : 默認(rèn)是并發(fā)的
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 循環(huán)的向隊(duì)列中添加10個(gè)操作
    for (int i = 0; i < 10; i++) {
        // 操作對象 : OP中的操作對象默認(rèn)是異步執(zhí)行
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo:) object:@(i)];
        // 將操作添加到隊(duì)列
        [queue addOperation:op];
    }
}
  • 執(zhí)行效果 : 會(huì)開啟多條線程,不是順序執(zhí)行.與GCD中并發(fā)隊(duì)列&異步執(zhí)行效果一樣
  • 隊(duì)列 : 默認(rèn)是并發(fā)的

NSBlockOperation

NSBlockOperation 基本使用演練

NSBlockOperation 操作添加到隊(duì)列

- (void)opDemo1
{
    // 隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 操作 : 默認(rèn)是異步的
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        // 查看當(dāng)前線程
        NSLog(@"%@",[NSThread currentThread]);
    }];
    // 將操作添加到隊(duì)列
    [queue addOperation:op];

    // 在當(dāng)前線程執(zhí)行
//    [op start];
}

NSBlockOperation 驗(yàn)證隊(duì)列并發(fā)性

- (void)opDemo2
{
    // 隊(duì)列 : 默認(rèn)是并發(fā)
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    for (int i = 0; i < 10; i++) {
        // 操作 : 默認(rèn)是異步的
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            // 查看當(dāng)前線程
            NSLog(@"%d %@",i,[NSThread currentThread]);
        }];
        // 將操作添加到隊(duì)列
        [queue addOperation:op];
    }
}

開發(fā)建議

  • NSOperationQueue只有一種類型.就是并發(fā)隊(duì)列.
  • 在實(shí)際開發(fā)時(shí),如果要使用到NSOperationQueue,可以直接定義成全局的隊(duì)列
/// 定義全局隊(duì)列
@property (nonatomic,strong) NSOperationQueue *queue;
- (NSOperationQueue *)queue
{
    if (_queue==nil) {
        _queue = [[NSOperationQueue alloc] init];
    }
    return _queue;
}

NSBlockOperation 簡寫

- (void)opDemo3
{
    [self.queue addOperationWithBlock:^{
        // 查看當(dāng)前線程
        NSLog(@"%@",[NSThread currentThread]);
    }];
}

線程間通信

- (void)opDemo5
{
    [self.queue addOperationWithBlock:^{
        NSLog(@"努力下載中...%@",[NSThread currentThread]);

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"更新UI...%@",[NSThread currentThread]);
        }];
    }];
}

NSOperation與GCD對比

GCD

  • 核心概念 : 將任務(wù)(block)添加到隊(duì)列(串行/并發(fā)/主隊(duì)列),并且指定任務(wù)執(zhí)行的函數(shù)(同步/異步).
  • GCD是C語言的API.
  • iOS 4.0 推出的,針對多核處理器的并發(fā)技術(shù).
  • 任務(wù)封裝在block中.
  • 要停止已經(jīng)加入 隊(duì)列(queue)任務(wù)(block) 需要寫復(fù)雜的代碼.
  • 只能設(shè)置隊(duì)列的優(yōu)先級.
  • 建立任務(wù)間的依賴關(guān)系比較復(fù)雜.
  • 高級功能 :
    • 一次性 once
    • 延遲操作 after
    • 調(diào)度組

NSOperation

  • 核心概念:把操作(異步)添加到隊(duì)列(并發(fā)隊(duì)列)
  • OC 框架,更加面向?qū)ο?,是?GCD 的封裝.
  • iOS 2.0 推出的,蘋果推出 GCD 之后,對NSOperation的底層全部重寫
  • 任務(wù)封裝在Operation對象中的,為我們提供了更多的選擇.操作對象更加方便.
  • 可以取消掉隊(duì)列中的任務(wù),正在執(zhí)行的任務(wù)除外.
  • 可以設(shè)置隊(duì)列中每一個(gè)操作的優(yōu)先級.
  • 可以跨隊(duì)列設(shè)置操作的依賴關(guān)系.
  • 高級功能 :
    • 最大操作并發(fā)數(shù)(GCD不好做)
    • 繼續(xù)/暫停/全部取消
    • 跨隊(duì)列設(shè)置操作的依賴關(guān)系

NSOperation高級功能演練

  • 最大操作并發(fā)數(shù)
  • 繼續(xù)/暫停/取消全部
  • 操作的優(yōu)先級和監(jiān)聽操作執(zhí)行完成的回調(diào)
  • 操作間依賴關(guān)系

隊(duì)列的最大并發(fā)數(shù)

  • 隊(duì)列的一個(gè)屬性.
  • @property NSInteger maxConcurrentOperationCount;
  • 限制同時(shí)執(zhí)行的任務(wù)數(shù).
  • 比如,最大并發(fā)數(shù)設(shè)置成3,CPU就最多準(zhǔn)備3個(gè)線程同時(shí)執(zhí)行3個(gè)任務(wù).
  • 線程可以復(fù)用.而且在線程回收的間隙可以及時(shí)的準(zhǔn)備線程保證并發(fā)性.

*** 準(zhǔn)備隊(duì)列**

/// 定義全局隊(duì)列
@property (nonatomic,strong) NSOperationQueue *queue;
  • 懶加載的時(shí)候設(shè)置最大并發(fā)數(shù)
- (NSOperationQueue *)queue
{
    if (_queue==nil) {
        _queue = [[NSOperationQueue alloc] init];

        // 設(shè)置最大并發(fā)數(shù) : 每次只能調(diào)度兩個(gè)操作執(zhí)行
        _queue.maxConcurrentOperationCount = 2;
    }
    return _queue;
}
  • 演示最大并發(fā)數(shù)對隊(duì)列調(diào)度任務(wù)的影響
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self opDemo];
}
- (void)opDemo
{
    NSLog(@"start");

    for (int i = 0; i < 20; i++) {
        [self.queue addOperationWithBlock:^{

            // 休眠一秒鐘,演示最大并發(fā)數(shù)的效果更好
            [NSThread sleepForTimeInterval:1.0];

            NSLog(@"%d %@",i,[NSThread currentThread]);
        }];
    }
}

執(zhí)行的結(jié)果 : 任務(wù)是兩個(gè)兩個(gè)的執(zhí)行.

隊(duì)列的暫停繼續(xù)和取消全部

  • 在最大并發(fā)數(shù)的代碼基礎(chǔ)上演示隊(duì)列的暫停繼續(xù)和取消全部

相關(guān)的屬性和方法介紹

  • - (BOOL)isSuspended;暫停和繼續(xù)隊(duì)列的屬性.
  • YES代表暫停隊(duì)列,NO代表恢復(fù)隊(duì)列.
  • cancelAllOperations : 取消隊(duì)列中的全部操作.
  • cancel : 取消隊(duì)列中的單個(gè)操作.

1. 隊(duì)列暫停

#pragma mark - 演示隊(duì)列的暫停
- (IBAction)stop:(id)sender
{
    // 暫停隊(duì)列之前判斷隊(duì)列中有無操作
    if (self.queue.operationCount == 0) {
        return;
    }

    // 暫停隊(duì)列
    self.queue.suspended = YES;
    NSLog(@"暫停 %zd",self.queue.operationCount);
}
  • 將隊(duì)列掛起之后,隊(duì)列中的操作就不會(huì)被調(diào)度,但是正在執(zhí)行的操作不受影
  • operationCount:操作計(jì)數(shù),沒有執(zhí)行和沒有執(zhí)行完的操作,都會(huì)計(jì)算在操作計(jì)數(shù)之內(nèi)

注意 : 如果先暫停隊(duì)列,再添加操作到隊(duì)列,隊(duì)列不會(huì)調(diào)度添加的操作.所以在暫停隊(duì)列之前要判斷隊(duì)列中有沒有任務(wù).如果沒有任務(wù)就不暫停隊(duì)列.

2. 隊(duì)列繼續(xù)

- (IBAction)resume:(id)sender
{
    // 隊(duì)列繼續(xù)
    self.queue.suspended = NO;
    NSLog(@"繼續(xù) %zd",self.queue.operationCount);
}

3. 隊(duì)列取消全部

- (IBAction)cancelAll:(id)sender
{
    [self.queue cancelAllOperations];
    NSLog(@"取消全部 %zd",self.queue.operationCount);
}
  • 一旦調(diào)用的 cancelAllOperations方法,隊(duì)列中的操作,都會(huì)被移除,正在執(zhí)行的操作除外.
  • 正在執(zhí)行的操作取消不了,如果要取消,需要自定義隊(duì)列.

操作優(yōu)先級和監(jiān)聽操作完成回調(diào)

  • 操作的優(yōu)先級 : qualityOfService
    • 無法決定操作執(zhí)行的先后順序的,決定的是操作有更多的機(jī)會(huì)被隊(duì)列調(diào)度執(zhí)行.
  • 監(jiān)聽操作完成的回調(diào) : @property (nullable, copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0);
    • 當(dāng)操作執(zhí)行結(jié)束之后,就會(huì)回調(diào),是在子線程中執(zhí)行的.
- (void)opDemo
{
    // 操作1
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 10; i++) {
            // 查看當(dāng)前線程
            NSLog(@"op1 %@",[NSThread currentThread]);
        }
    }];

    //設(shè)置操作的優(yōu)先級
    op1.qualityOfService = NSQualityOfServiceUserInteractive;

    // 當(dāng)操作執(zhí)行結(jié)束之后,就會(huì)回調(diào),是在子線程中執(zhí)行的
    [op1 setCompletionBlock:^{
        // 查看當(dāng)前線程
        NSLog(@"操作結(jié)束了 %@",[NSThread currentThread]);
    }];

    [self.queue addOperation:op1];

    // 操作2
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 10; i++) {
            // 查看當(dāng)前線程
            NSLog(@"op2 %@",[NSThread currentThread]);
        }
    }];
    op2.qualityOfService = NSQualityOfServiceBackground;
    [self.queue addOperation:op2];
}

操作間依賴

操作op2依賴于op1
[op2 addDependency:op1];

4. 操作依賴

需求 : 登陸-->付費(fèi)-->下載-->通知用戶

  • 準(zhǔn)備需要執(zhí)行的操作
#pragma mark - 操作依賴
- (void)dependency
{
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"登陸 %@",[NSThread currentThread]);
    }];

    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"付費(fèi) %@",[NSThread currentThread]);
    }];

    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"下載 %@",[NSThread currentThread]);
    }];

    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"通知用戶 %@",[NSThread currentThread]);
    }];
}
  • 建立依賴關(guān)系 : 不能循環(huán)建立操作間依賴關(guān)系.否則,隊(duì)列不調(diào)度操作執(zhí)行
// 操作2依賴操作1
[op2 addDependency:op1];
[op3 addDependency:op2];
[op4 addDependency:op3];

// 不能循環(huán)依賴 : 操作不會(huì)被調(diào)度
// [op1 addDependency:op4];

// waitUntilFinished : 是否等到指定的操作執(zhí)行結(jié)束再執(zhí)行后面的代碼
[self.queue addOperations:@[op1,op2,op3,op4] waitUntilFinished:NO];

// 驗(yàn)證 waitUntilFinished
NSLog(@"end");
  • 建立依賴關(guān)系 : 操作間可以跨隊(duì)列建立依賴關(guān)系
// 操作2依賴操作1
[op2 addDependency:op1];
[op3 addDependency:op2];
[op4 addDependency:op3];

// 不能循環(huán)依賴 : 操作不會(huì)被調(diào)度
// [op1 addDependency:op4];

// waitUntilFinished : 是否等到指定的操作執(zhí)行結(jié)束再執(zhí)行后面的代碼
[self.queue addOperations:@[op1,op2,op3] waitUntilFinished:NO];

// 通知用戶的操作在主線程中執(zhí)行
// 操作可以跨隊(duì)列依賴
[[NSOperationQueue mainQueue] addOperation:op4];

// 驗(yàn)證 waitUntilFinished
NSLog(@"end");
  • 建立依賴關(guān)系 : 要將操作間的依賴建立好了之后,再添加到隊(duì)列中
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容