基於NSOperation的序列執行緒
阿新 • • 發佈:2019-01-04
背景介紹:在接入七牛SDK的時候,發現SDK沒有批量上傳圖片的介面,業務又涉及到了上傳進度統計,並且要求一次性的圖片完整上傳。
開始的時候打算用GCD,寫著寫著感覺擴充套件性不好,可讀性不高,取消機制也不是很友好,執行緒不能暫停,於是改成了NSBlockOperation來實現
.h檔案
/** 序列佇列 @param dataList 資料來源 @param opreationBlock 執行回撥 @param cancelBlock 中斷回撥 @param completionBlock 完成回撥 @return 執行緒佇列 */ + (NSOperationQueue *)startOperationOnSerialQueue:(NSArray *)dataList opreationBlock:(void(^)(id data, NSOperationQueue *queue, NSInteger index, double totalProgress))opreationBlock cancelBlock:(BOOL(^)(id data, NSInteger index))cancelBlock completionBlock:(void(^)(BOOL success))completionBlock;
介紹下h檔案,這個類方法可以建立一個執行緒池,序列佇列的順序由dataList陣列源中的元素順序決定,提供三種不同時機的回撥,根據實際情況使用,opreationBlock是主要回調,相當於thread的main。
.m檔案
+ (NSOperationQueue *)startOperationOnSerialQueue:(NSArray *)dataList opreationBlock:(void(^)(id data, NSOperationQueue *queue, NSInteger index, double totalProgress))opreationBlock cancelBlock:(BOOL(^)(id data, NSInteger index))cancelBlock completionBlock:(void(^)(BOOL success))completionBlock { // 建立一個執行緒佇列,最大併發數為1 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.maxConcurrentOperationCount = 1; // 根據資料來源建立Operation __block NSBlockOperation *lastBlockOperation; [dataList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { __block NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ // 如果設定了中斷回撥,優先檢測中斷回撥是否觸發 if (cancelBlock) { BOOL cancelFlag = cancelBlock(obj,idx); //如果中斷了 if (cancelFlag) { //取消所有執行緒 [queue cancelAllOperations]; // 觸發一次完成回撥 [queue addOperation:[NSBlockOperation blockOperationWithBlock:^{ if (completionBlock) { completionBlock(NO); } }]]; } else { if (opreationBlock) { opreationBlock(obj, queue, idx, idx*1.0f/dataList.count); } } } else { if (opreationBlock) { opreationBlock(obj, queue, idx, idx*1.0f/dataList.count); } } }]; // 將當前執行緒設定為上一次執行緒的依賴,保證執行順序 if (lastBlockOperation) { [blockOperation addDependency:lastBlockOperation]; } lastBlockOperation = blockOperation; [queue addOperation:blockOperation]; if (idx == dataList.count-1) { //當最後一個執行緒執行完畢後,觸發完成回撥 NSBlockOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{ if (completionBlock) { completionBlock(YES); } }]; [completionOperation addDependency:blockOperation]; [queue addOperation:completionOperation]; } }]; return queue; }
流程都有註釋,主要邏輯就是將執行緒設定成單項依賴,然後在適當的時機執行中斷判斷,觸發完成回撥。到現在為止完成了同步操作的序列佇列執行,但大家都知道,同步操作即使不用佇列,也是序列執行的,所以這裡要解決非同步操作的序列佇列執行,這就用到了NSOperationQueue的suspended功能,在執行非同步操作前先暫停佇列,等非同步操作完成後,再繼續佇列。
具體實現參考
NSMutableArray *imageKeys = [NSMutableArray array]; __block NSString *lastResultKey = nil; [GinOperationQueueManager startOperationOnSerialQueue:imageList opreationBlock:^(id data, NSOperationQueue *queue, NSInteger index, double totalProgress) { // 這裡掛起佇列 queue.suspended = YES; [self uploadSingleImage:data filename:[CEQiNiuManager createJPGImageKey] progress:nil completion:^(QNResponseInfo *info, NSString *key, NSDictionary *resp, UIImage *image) { lastResultKey = key; if (!key) { if (completion) { completion(NO, nil); } } else { if (progress) { progress(key, totalProgress ,index); } [imageKeys addObject:key]; } //執行完成後繼續佇列 queue.suspended = NO; }]; } cancelBlock:^BOOL(id data, NSInteger index) { return ![lastResultKey isNotBlank] && index != 0; } completionBlock:^(BOOL success) { if (completion) { completion(success, imageKeys.count!=0?imageKeys:nil); } }];
這裡是原始檔,供大家參考
DEMO地址