ネットワーク リクエストとユーザー インタラクションの処理中にデータを変更する複数のアクターをサポートできるアプリケーションの完全な例を次に示します。
スレッドセーフに関連するサービスに触れるときは、シリアルdispatch_queue_tにリンクされたBFExecutorを使用するというガイドラインを自分自身に設定しました。
他のベストプラクティスを指摘することは役に立ちます、ありがとう!
#import <Bolts/Bolts.h>
dispatch_queue_t serialQueue;
BFExecutor *serialExecutor;
BFTask *maintask;
int32_t key = 0;
BOOL initialized = FALSE;
@implementation yAppDelegate
- (void)setKey:(int32_t)key_ {
// Don't worry about success, just appending to outstanding tasks
maintask = [maintask continueWithExecutor:serialExecutor withBlock:^id(BFTask *task) {
BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
key = key_;
NSLog(@"set key to %d", key);
[source setResult:nil];
if (initialized) {
[self initialize];
}
return source.task;
}];
}
- (BFTask *)downloadConfig {
BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
dispatch_async(serialQueue, ^{
NSLog(@"working on init config, key = %d...", key);
// Trigger from a different queue
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[source setResult:nil];
});
});
return source.task;
}
- (BFTask *)initializeService {
BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
dispatch_async(serialQueue, ^{
NSLog(@"working on init service, key = %d...", key);
// Trigger from a different queue
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[source setResult:nil];
});
// Set final state
initialized = TRUE;
});
return source.task;
}
- (void)initialize {
int32_t oldKey = key;
__block bool reinit = false;
// Start by chaining it to whatever task is in flight without regard to success
// Everything should use the serialQueue or serialExecutor for thread safety
maintask = [[[maintask continueWithExecutor:serialExecutor withBlock:^id(BFTask *task) {
if (oldKey != key) {
NSLog(@"key out of date (%d != %d). reinitializing...", oldKey, key);
reinit = true;
return [BFTask cancelledTask];
}
return [self downloadConfig];
}] continueWithExecutor:serialExecutor withSuccessBlock:^id(BFTask *task) {
if (oldKey != key) {
NSLog(@"key out of date (%d != %d). reinitializing...", oldKey, key);
reinit = true;
return [BFTask cancelledTask];
}
return [self initializeService];
}] continueWithExecutor:serialExecutor withBlock:^id(BFTask *task) {
if (oldKey != key) {
NSLog(@"key out of date (%d != %d). reinitializing...", oldKey, key);
reinit = true;
}
if (task.error || task.exception || task.isCancelled) {
if (reinit) {
[self initialize];
}
return nil;
} else {
NSLog(@"initService completed = %d", key);
return nil;
}
}];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
serialQueue = dispatch_queue_create("serial", NULL);
serialExecutor = [BFExecutor executorWithDispatchQueue:serialQueue];
// Start with an empty task so all tasks can be strung together without need to initialize
maintask = [BFTask taskWithResult:nil];
// Everything related to changing service state should be contained with the serialQueue dispatch queue
[self setKey:1];
[self initialize];
[self setKey:2];
[self setKey:3];
dispatch_async(dispatch_get_main_queue(), ^(void){
[self setKey:4];
});
dispatch_async(serialQueue, ^(void){
[self setKey:5];
});
[self setKey:6];
// Override point for customization after application launch.
return YES;
}
@end
結果は予想通りです。key=1 で初期化を試みますが、変化しなくなるまで違います。その後、サービスはキー = 5 でキュー上で初期化され、キー = 4 で再初期化されます。
結果:
set key to 1
key out of date (0 != 1). reinitializing...
key out of date (0 != 1). reinitializing...
set key to 2
set key to 3
set key to 6
set key to 5
key out of date (1 != 5). reinitializing...
key out of date (1 != 5). reinitializing...
working on init config, key = 5...
working on init service, key = 5...
initService completed = 5
set key to 4
working on init config, key = 4...
working on init service, key = 4...
initService completed = 4