【Objective-C】NSOperationQueueを活用した「非同期的に同期的処理(forなど)」を行う方法について【Xcode11/iOS13対応】
こういう人に向けて発信しています。
・同期的な処理を非同期的に行いたい人
・ディスパッチキューを使用せずに非同期処理を行いたい人
・Objective-C中級者
補足ですが、特定の非同期処理の後に何かの処理を行いたいという場合は、
NSOperationQueueを活用すれば容易に行えます。
(こちらに関しては別記事にまとめましたのでマガジン一覧からご確認ください)
以前の記事(GCD ディスパッチキュー採用)
以前の記事内で
同期的処理を非同期的に処理する方法についても触れています。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
//下記for構文に関しては本来は同期処理。
for (int i = 0; i < 3; i++) {
NSLog(@"検証用:for構文");
}
});
このような書き方でも概ね問題ないのですが、
メソッドAとメソッドBで同じタイミングで非同期処理を、
同じように処理している場合は終了するタイミングは不確定となります。
(for文であれば件数や処理の重さにより、処理が終了した途端という話になります)
コード(Objective-C)
先にコード見せましょうか。
#import "ViewController.h"
#import "DataManager.h"
#import "DemoViewController.h"
@interface UIViewController()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//NSOperationQueueを初期化する。
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//同時に並列処理するオペレーションタスクの上限数を設定する。
queue.maxConcurrentOperationCount = 2; //特に設定しなければシステム側が判定して並列処理数を確定させる。
//オペレーションA初期化:5回ログを吐き出す処理を呼び出す
NSOperation *opA = [NSBlockOperation blockOperationWithBlock:^{
[self roopA];
}];
//オペレーションB初期化:xibの背景色を黄色にだけ変更したかUIViewControllerをモーダル表示し、表示完了した直後に画面を下げる処理
NSOperation *opB = [NSBlockOperation blockOperationWithBlock:^{
dispatch_async(dispatch_get_main_queue(), ^{
//デモVCを上げて、上げ終わった後下げる処理
DemoViewController *demoVC = [[DemoViewController alloc]init];
[self presentViewController:demoVC animated:YES completion:^{
[self dismissViewControllerAnimated:YES completion:nil];
NSLog(@"B処理完了");
}];
});
}];
//オペレーションC初期化:ネットワーク通信シングルトンを参照し、URLSessionを用いて同期的に通信結果を取得する処理
NSOperation *opC = [NSBlockOperation blockOperationWithBlock:^{
DataManager *dataManager = [DataManager sharedManager];
NSDictionary *dic = [dataManager sessionSyncRequest:@"https://www.json-generator.com/api/json/get/cezpmhLKZK?indent=2"];
NSLog(@"C処理完了");
}];
//オペレーションタスクAをオペレーションキューに追加する。
[queue addOperation:opA];
//オペレーションタスクBをオペレーションキューに追加する。
[queue addOperation:opB];
//オペレーションタスクCをオペレーションキューに追加する。
[queue addOperation:opC];
}
- (void)roopA{
//5回 logを吐き出す処理
for(int i=0; i<5; i++){
NSLog(@"A roop");
[NSThread sleepForTimeInterval:0.2];
}
}
@end
上記コードでビルドした際のログ
2019-11-27 20:59:29.986844+0900 root_projectFile[15169:448730] A roop
2019-11-27 20:59:30.187238+0900 root_projectFile[15169:448730] A roop
2019-11-27 20:59:30.387830+0900 root_projectFile[15169:448730] A roop
2019-11-27 20:59:30.595830+0900 root_projectFile[15169:448730] A roop
2019-11-27 20:59:30.721343+0900 root_projectFile[15169:448669] B処理完了
2019-11-27 20:59:30.800451+0900 root_projectFile[15169:448730] A roop
2019-11-27 20:59:30.874381+0900 root_projectFile[15169:448729] C処理完了
つまり、for文が同期的な処理が行われていないことが分かります。