Skip to content

Instantly share code, notes, and snippets.

@roothybrid7
Last active August 29, 2015 14:14
Show Gist options
  • Save roothybrid7/f55c162814bcde6865df to your computer and use it in GitHub Desktop.
Save roothybrid7/f55c162814bcde6865df to your computer and use it in GitHub Desktop.
実行中のNSOperationに考慮しつつ、NSOperationQueueのキャンセルを実行する ref: http://qiita.com/roothybrid7/items/83db3809775295ea6df3
// DataParseOperationQueue.h
#import <Foundation/Foundation.h>
@interface DataParseOperationQueue : NSOperationQueue
// XXX: 全ての実行中の`NSOperation`が終わるまで待ち、完了したらcompletionHandler blocksを実行する
- (void)cancelAllOperationsWithCompletionHandler:(void(^)(void))completionHandler;
@end
// DataParseOperationQueue.m
#import "DataParseOperationQueue.h"
static NSString *const SynchronizedQueueLabelPrefix = @"com.rh7.synchronized.DataParseOperationQueue";
@interface ParseOperationQueue ()
// XXX: 同時アクセスを防止するためのシリアルキュー(@synchronizedでも可)
@property (nonatomic) dispatch_queue_t synchronizedQueue;
@end
@implementation ParseOperationQueue
- (instancetype)init {
self = [super init];
if (self) {
const char *synchronized_queue_label = [[SynchronizedQueueLabelPrefix stringByAppendingPathExtension:[[NSUUID UUID] UUIDString]] UTF8String];
_synchronizedQueue = dispatch_queue_create(synchronized_queue_label, DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)cancelAllOperationsWithCompletionHandler:(void(^)(void))completionHandler {
dispatch_sync(self.synchronizedQueue, ^{
// XXX: 実行待ちの`NSOperation`を実行されないように停止する
self.suspended = YES;
// XXX: 実行待ちの`NSOperation`は全てキャンセルする
NSArray *pendingOperations = [self.operations filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%K == %@", @"isExecuting", @NO]];
[pendingOperations enumerateObjectsUsingBlock:^(NSOperation *op, NSUInteger idx, BOOL *stop) {
[op cancel];
}];
// XXX: 実行中の`NSOperation`が全て完了してから、`completionHandler`を一番最後に実行するため、同時実行並列数を1にする
NSUInteger currentMaxConcurrentCount = self.maxConcurrentOperationCount;
self.maxConcurrentOperationCount = 1;
// XXX: 再開
self.suspended = NO;
// XXX: このOperationは必ず最後に実行される
NSBlockOperation *waitForCompletion = [NSBlockOperation blockOperationWithBlock:^{
if (completionHandler) {
completionHandler(); // 呼び出し元から渡されたblocksを実行
}
// XXX: 設定を元に戻す
self.maxConcurrentOperationCount = currentMaxConcurrentCount;
}];
[self addOperation:waitForCompletion];
});
}
@end
#import "DataParseOperationQueue.h"
@interface AccountManger ()
@property (nonatomic, readonly) DataParseOperationQueue *operationQueue;
@end
@implementation AccountManager
- (void)cancelAllOperationsWithCompletionHandler:(void(^)(void))completionHandler {
dispatch_sync(self.synchronizedQueue, ^{
DataParseOperationQueue *operationQueue = self.operationQueue;
self.operationQueue = nil; // XXX: 新しい処理を追加されないように、 一旦他から参照させないようにする
// XXX: Pendingのままにして、実行していないオペレーションをすべてキャンセルする
[operationQueue cancelAllOperationsWithCompletionHandler:^{
// XXX: 元に戻す
self.operationQueue = operationQueue;
if (completionHandler) {
completionHandler();
}
}];
});
}
@end
- (PMKPromise *)waitAllOperations {
PMKPromise *accountManagerCancelAllOperations = [PMKPromise new:^(PMKPromiseFulfiller fulfill, PMKPromiseRejecter reject) {
[[AccountManager defaultManager] cancelAllOperationsWithCompletionHandler:^{ fulfill(nil); }];
}];
PMKPromise *apiResourceManagerCancelAllOperations = [PMKPromise new:^(PMKPromiseFulfiller fulfill, PMKPromiseRejecter reject) {
[[APIResourceManager defaultManager] cancelAllOperationsWithCompletionHandler:^{ fulfill(nil); }];
}];
return [PMKPromise when:@[accountManagerCancelAllOperations, organizationCancelAllOperations]];
}
- (void)logout {
[APISessionManager sharedManager] resetSession];
[self waitAllOperations].finally(^{
// XXX: すべてのOperationQueueのキャンセルが完了したら、ログアウト処理を実行する。
[self closeSessionWithNotification];
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment