NSOperation

NSOperation表示了一個獨立的計算單元。作為一個抽象類,它給了它的子類一個十分有用而且線程安全的方式來建立狀態(tài)、優(yōu)先級、依賴性和取消等的模型。你可以使用系統(tǒng)提供的NSBlockOperationNSInvocationOperation方法來創(chuàng)建一個operation,也可以創(chuàng)建一個繼承NSOperation抽象類的operation。

異步vs同步Operations

Operations分為同步和異步。創(chuàng)建的operations默認(rèn)是一個同步的operation。如果你調(diào)用start方法啟動一個operation,它會在當(dāng)前線程執(zhí)行,并且阻塞當(dāng)前線程直到這個operation結(jié)束。另外,你可以把operations放入operation queue中。放入operation queue中的operations會另起一個線程調(diào)用start方法,因此會異步執(zhí)行。當(dāng)然你可以創(chuàng)建一個異步的operation,這需要重寫很多方法,比較麻煩,建議是直接放入operation queue中。

NSOperation的狀態(tài)

NSOperation包含了一個狀態(tài)機來描述每一個操作的執(zhí)行。

  • isReady 已經(jīng)準(zhǔn)備好執(zhí)行
  • isExecuting 正在執(zhí)行
  • isFinished 執(zhí)行成功或取消
    這三種狀態(tài)是相互獨立的,同時只能是一個狀態(tài)屬性返回YES。這些狀態(tài)由keypath的KVO通知決定。

NSOperation依賴性

如果某些operation需要按照一定的次序執(zhí)行。則可以通過addDependency為相應(yīng)的隊列添加依賴。但是在添加依賴的時候要注意依賴循環(huán),從而導(dǎo)致死循環(huán)。

NSOperation優(yōu)先級

你可以通過operation的queuePriority來設(shè)置優(yōu)先級,從而加快或者延遲queue中的operation的執(zhí)行。默認(rèn)的queuePriorityNSOperationQueuePriorityNormal。

  • NSOperationQueuePriorityVeryLow
  • NSOperationQueuePriorityLow
  • NSOperationQueuePriorityNormal
  • NSOperationQueuePriorityHigh
  • NSOperationQueuePriorityVeryHigh

但是priority不能與dependency一起使用。添加了dependency的operation一定嚴(yán)格按照dependency的順序執(zhí)行。
同時你可以通過operation的qualityOfService來設(shè)置系統(tǒng)資源對operation的保障。高保障的operation的優(yōu)先級大于低保障的operation的優(yōu)先級。默認(rèn)的保障等級是NSQualityOfServiceBackground。

  • NSQualityOfServiceUserInteractive
  • NSQualityOfServiceUserInitiated
  • NSQualityOfServiceUtility
  • NSQualityOfServiceBackground

NSOperation用法

首先介紹以下NSBlockOperation和NSInvocationOperation。代碼如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSOperation *blkOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blk start");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"blk finished");
    }];
    blkOperation.queuePriority = NSOperationQueuePriorityHigh;
    blkOperation.qualityOfService = NSQualityOfServiceUserInitiated;
    NSMutableArray *arr = [NSMutableArray arrayWithArray:@[@1,@2]];
    NSOperation *invoOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomethingWithArr:) object:arr];
    NSLog(@"question");
    [queue addOperation:blkOperation];
    blkOperation.completionBlock = ^{
        NSLog(@"haha");
    };
    [NSThread sleepForTimeInterval:1];
    [blkOperation cancel];
    [queue addOperation:invoOperation];
    [blkOperation waitUntilFinished];
    NSLog(@"answer");

}

- (void)doSomethingWithArr:(NSMutableArray *)arr {
    NSLog(@"arr is %@ in invo",arr);

}

執(zhí)行結(jié)果:

2018-01-05 09:13:31.205077+0800 NSOperationDemo[25280:6903063] question
2018-01-05 09:13:31.205314+0800 NSOperationDemo[25280:6903118] blk start
2018-01-05 09:13:32.206698+0800 NSOperationDemo[25280:6903116] arr is (
1,
2
) in invo
2018-01-05 09:13:34.209701+0800 NSOperationDemo[25280:6903118] blk finished
2018-01-05 09:13:34.209970+0800 NSOperationDemo[25280:6903063] answer
2018-01-05 09:13:34.209981+0800 NSOperationDemo[25280:6903116] haha

當(dāng)blkOperation被加入到queue時,這個blkOperation才會被執(zhí)行。加入queue中的operations是并發(fā)執(zhí)行的。所以invoOperation會同時和blkOperation一起執(zhí)行。不過由于queue遵循FIFO原則,所以一般會blkOperation先于invoOperation執(zhí)行。由于blkOperation的cancel在1s后執(zhí)行,此時由于blkOperation已經(jīng)在執(zhí)行,所以無法取消。waitUntilFinished必須是在加入queue后才能執(zhí)行,不然會死鎖。waitUntilFinished后面的代碼會在當(dāng)前operation執(zhí)行完之后才會執(zhí)行。completionBlock表示這個opertion執(zhí)行完之后做的處理。

接下來介紹一下自定義并發(fā)的operation。代碼如下:

@interface AWOperation() {
BOOL executing;
BOOL finished;
}

@end

@implementation AWOperation

- (instancetype)init {
    if (self = [super init]) {
        executing = NO;
        finished = NO;
    }
    return self;
}

- (void)start {
    if (self.isCancelled) {
        [self willChangeValueForKey:@"isFinished"];
        finished = YES;
        [self didChangeValueForKey:@"isFinished"];
        return;
    }

    [self willChangeValueForKey:@"isExecuting"];
    [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
    executing = YES;
    [self didChangeValueForKey:@"isExecuting"];
}

- (void)main {
    @try {
        [self willChangeValueForKey:@"isExecuting"];
        executing = NO;
        [self didChangeValueForKey:@"isExecuting"];

        [self willChangeValueForKey:@"isFinished"];
        finished = YES;
        [self didChangeValueForKey:@"isFinished"];
    } @catch (NSException *exception) {
        NSLog(@"Exception: %@", exception);
    }
}

- (BOOL)isExecuting {
    return executing;
}

- (BOOL)isFinished {
    return finished;
}

- (BOOL)isConcurrent {
    return YES;
}

對于自定義oepration,start, isExecuting, isFinished, isConcurrent方法是必須實現(xiàn)的。main方法不是必須實現(xiàn)的,但是為了結(jié)構(gòu)清晰,一般會把要做的任務(wù)放在main函數(shù)中。
start方法是operation的執(zhí)行起點。但是當(dāng)這個operation被cancel掉時,也需要設(shè)置operation的狀態(tài)為finished。對于并發(fā)的operation,就是另外啟動一個線程來執(zhí)行main方法,同時isConcurrent方法一直返回YES。
另外我們得自己維護operation的狀態(tài),同時觸發(fā)相應(yīng)的KVO通知。

NSOperation VS GCD

NSOperation是對GCD的封裝。使用NSOperation也就是在使用GCD。由于NSOperation是更高級的API,因此它擁有更多的功能。

  • 依賴性
  • 可觀察狀態(tài)
  • 停止,取消,啟動
  • 控制并發(fā)

雖然蘋果建議使用更高級的API,但是如果GCD能夠滿足要求的話,還是建議用GCD,因為它更輕量。如果需要按照一定順序執(zhí)行或者其他高要求的話,可以使用NSOpertation。

參考

Operation
Choosing Between NSOperation and Grand Central Dispatch
iOS 并發(fā)編程之 Operation Queues
iOS多線程之NSOperation和NSOperationQueue

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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