通信処理など時間が掛かる処理を行いたいが、一瞬で完了してはUI的に困る。最低xx秒は処理の完了を待ってほしいときに以下のように呼び出す。
下の場合、A点の処理が一瞬で完了したとしても、最低0.8秒はB点の処理の実行は待機される。
sender.enabled = NO;
[NSObject perform:^(NSNotifyHandler done) {
//A 通信などの処理
[self hogeWithCompletion:^{
done(^{
//B 完了した時に行う動作
NSLog(@"done");
sender.enabled = YES;
});
}];
} waitUntil:0.8];
実装
実装には以前こちらで紹介されていたSTDeferredライブラリを利用させて頂きました。
NSObject+AsyncWaitUntil.h
typedef void(^NSNotifyHandler)(void (^completion)());
@interface NSObject (AsyncWaitUntil)
+ (void)perform:(void (^)(NSNotifyHandler done))task waitUntil:(NSTimeInterval)timeInterval;
@end
NSObject+AsyncWaitUntil.m
#import "NSObject+AsyncWaitUntil.h"
#import <STDeferred/STDeferred.h>
@implementation NSObject (AsyncWaitUntil)
+ (void)perform:(void (^)(NSNotifyHandler))task waitUntil:(NSTimeInterval)timeInterval
{
NSAssert(task, @"NSNotifyHandler must be non-nil.");
STDeferredBlock runTask = ^{
STDeferred *deferred = [STDeferred deferred];
NSNotifyHandler notify = ^(void (^completion)()) {
[deferred resolve:completion];
};
task(notify);
return deferred;
};
STDeferredBlock waitUntil = ^{
STDeferred *deferred = [STDeferred deferred];
int64_t delayInMilliSeconds = timeInterval * 1000;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInMilliSeconds * NSEC_PER_MSEC);
dispatch_after(popTime, dispatch_get_current_queue(), ^(void){
[deferred resolve:nil];
});
return deferred;
};
[[STDeferred when:runTask, waitUntil, nil] then:^(id resultObject) {
id block = [resultObject objectAtIndex:0];
if (![block isKindOfClass:[NSNull class]]) {
void (^completion)() = block;
completion();
}
}];
}