概要
dispatch_onceを使えばインスタンスのライフサイクルで1度しか実行されないことを保証する機構ができます。
できませんでした。
詳細についてはコメント欄をどうぞ
背景
インスタンスが生成されてから一度だけ実行したい処理というのは出てくると思います。そんなの init でやればいいのですが、
状況によっては init の時には準備が整っておらず、任意のタイミングで処理を実行したいことがあると思います。
その時にフラグのプロパティを持つのはダサい、マルチスレッド面倒!という場合の解決策です。
概要
- インスタンスに
dispatch_once_t
型のプロパティを持つ - このプロパティを
dispatch_once
に渡して実行する
コード
@interface MyObject : NSObject
@property (nonatomic, assign) dispatch_once_t onceToken;
- (void)runOnce;
@end
@implementation MyObject
- (void)runOnce {
dispatch_once(&(_onceToken), ^{
// 一度しか実行しない処理をここに書く
});
}
@end
実験とその結果
以下のクラスに対して、処理を100回リクエストする処理を行いました。
@interface MyObject : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) dispatch_once_t onceToken;
@property (nonatomic, assign) NSInteger count;
- (void)runOnce;
@end
@implementation MyObject
+ (instancetype)objectForName:(NSString *)name {
MyObject *obj = [MyObject new];
obj.name = name;
return obj;
}
- (void)runOnce {
dispatch_once(&(_onceToken), ^{
_count++;
NSLog(@"%@ : here!!!", _name);
});
}
@end
MyObject *obj1 = [MyObject objectForName:@"obj1"];
MyObject *obj2 = [MyObject objectForName:@"obj2"];
dispatch_group_t group = dispatch_group_create();
for (NSInteger i = 0; i < 100; ++i) {
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[obj1 runOnce];
[obj2 runOnce];
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"%@ -> %ld", obj1.name, obj1.count);
NSLog(@"%@ -> %ld", obj2.name, obj2.count);
2014-09-03 19:27:08.895 project[28275:7400318] obj1 : here!!!
2014-09-03 19:27:08.896 project[28275:7400318] obj2 : here!!!
2014-09-03 19:27:08.896 project[28275:7400187] obj1 -> 1
2014-09-03 19:27:08.896 project[28275:7400187] obj2 -> 1
様々なスレッドからobj1, obj2に対してrunOnce
を呼んでいますがobj1, obj2のどちらも処理は一度しか行われていないのが分かります。
注意点
onceTokenは書き換え可能なのでインスタンス内などで上書きしないようにしましょう。
tokenの値が元に戻ると再び処理が実行されます。