iOS同時プログラミング(二)——NSOperation
前の記事では、NSThreadを使用するとマルチスレッドを実現できますが、スレッドの作成、メンテナンス、終了には、開発者自身が責任を負う必要があり、スレッドが多い場合、管理が困難になります.NSOperationは抽象クラスでtaskをカプセル化しており、直接インスタンス化することはできません.手動でOperationを管理したり、NSOperationQueueに追加したりすることができます.NSOperationQueueに追加されたOperationは、開発者が最下位のマルチスレッド実装の詳細に注目する必要はありません.
Cocoaは3つの異なるNSOperationを提供しています.
1. NSBlockOperation
Block Operationsは、Blockオブジェクトを用いてtaskを実行する方法を提供する.
2. NSInvocationOperation
その名の通り、Invocation Operationは、taskを実行するために通常のmethodを呼び出す方法を提供する.
NSOperationから継承し、NSOperationをカスタマイズします.NSOperationをカスタマイズする場合は、少なくともmainおよび/またはstart関数を書き換えます.isExecutingとisFinishedも書き換え、KVOで他のオブジェクトに通知する必要があります.また、カスタムOperationの初期化関数を指定する必要があります.
まず、Block Operationが同期して実行するコードをください.
NSLog出力の順序から、simpleOperationがmainthreadを塞いでいることがわかり、main threadはblock実行が完了してから実行するのを待っています.
Operationのデフォルトは、作成したスレッドでstartメソッドによって実行されます.simpleOperationをNSOperationQueueに追加して非同期実行を実現したり、Operationをカスタマイズして新しいスレッドをdetachして実行したりすることができます.
NSOperationQueue *queue = [[NSOperationQueuealloc] init];
[queue addOperation:simpleOperation];
1つのOperationをqueueに追加すると、手動でstartに行く必要はありません.queueが完了するので、queueはそれに追加されたすべてのOperationを管理します.自分でコントロールしたい場合は(推奨されていません)、detach threadの方法を採用することができます.
複数のOperationを追加する必要がある場合は、関数を呼び出します.
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait
同時に、queueで現在どのようなOperationがあるか、およびOperationの数を取得できます.
- (NSArray *)operations;
- (NSUInteger)operationCount;
queueの優先度を設定することもできます.
- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;
記憶しやすい名前をqueueに追加します.
- (void)setName:(NSString *)n ;
- (NSString *)name ;
一時停止queue:
- (void)setSuspended:(BOOL)b;
- (BOOL)isSuspended;
名前の通り、この操作はNSObjectオブジェクトでメソッド(method)を呼び出すことです.同じように、同期実行の状況を見てみましょう.
self.simpleOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(simpleOperationEntry:) object:simpleObject];
[self.simpleOperation start];
これによりsimpleOperationは、作成された現在のスレッド(一般的にmain thread)で実行され、その実行は現在のスレッドを塞ぐ.
もちろん、それほど実用的な意味はありませんが、queueにsimpleOperationを追加すると、現在のスレッドと並列に実行でき、効率が向上します.
上記のコードでは,2つのOperationはそれぞれのスレッドでmain threadと並列に実行され,この2つのOperationの間でも並列に実行される.queueでget、setの最大並列Operation数を指定できます.
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
では、secondOperationをfirstOperationの後に実行させるとしたら?つまりsecondOperationはfirstOperationの実行が完了するまで待たなければならない.1つのOperationは、依存するOperationの実行が完了してから実行されるのを待つ別のOperationsまたは複数のOperationsに依存することができる.Operationに依存を追加していない場合は、いつ実行するかに制御権がありません.
addDependency:メソッドは、queueに追加する前に2つのOperationに依存属性を追加する問題を解決します.
[self.secondOperation addDependency:self.firstOperation];
依存が不要になった場合はremoveDependency:依存を解除します.
[self.secondOperation removeDependency:self.firstOperation];
また、NSInvocationOperationは、指定した初期化関数で初期化することもできます.
- (id)initWithInvocation:(NSInvocation *)inv;//designated initializer
1つのOperationには、実行プロセスを識別するための適切なステータスがあります.
- (BOOL)isCancelled;
- (BOOL)isExecuting;
- (BOOL)isFinished;
- (BOOL)isConcurrent;
- (BOOL)isReady;
-(void)cancelでOperationをキャンセルできます.
queueでは、-(void)cancelAllOperationsですべてのOperationをキャンセルします.
Cocoaは3つの異なるNSOperationを提供しています.
1. NSBlockOperation
Block Operationsは、Blockオブジェクトを用いてtaskを実行する方法を提供する.
2. NSInvocationOperation
その名の通り、Invocation Operationは、taskを実行するために通常のmethodを呼び出す方法を提供する.
3. PlainOperation
NSOperationから継承し、NSOperationをカスタマイズします.NSOperationをカスタマイズする場合は、少なくともmainおよび/またはstart関数を書き換えます.isExecutingとisFinishedも書き換え、KVOで他のオブジェクトに通知する必要があります.また、カスタムOperationの初期化関数を指定する必要があります.
2.1 NSBlockOperation
まず、Block Operationが同期して実行するコードをください.
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
NSBlockOperation *simpleOperation = [NSBlockOperation blockOperationWithBlock:^{ NSUInteger counter = 0;
for (counter = 0; counter < 1000;counter++){
NSLog(@"Count = %lu", (unsigned long)counter);
}
}];
[simpleOperation start];
NSLog(@"Main thread is here");
return YES;
}
NSLog出力の順序から、simpleOperationがmainthreadを塞いでいることがわかり、main threadはblock実行が完了してから実行するのを待っています.
Operationのデフォルトは、作成したスレッドでstartメソッドによって実行されます.simpleOperationをNSOperationQueueに追加して非同期実行を実現したり、Operationをカスタマイズして新しいスレッドをdetachして実行したりすることができます.
NSOperationQueue *queue = [[NSOperationQueuealloc] init];
[queue addOperation:simpleOperation];
1つのOperationをqueueに追加すると、手動でstartに行く必要はありません.queueが完了するので、queueはそれに追加されたすべてのOperationを管理します.自分でコントロールしたい場合は(推奨されていません)、detach threadの方法を採用することができます.
複数のOperationを追加する必要がある場合は、関数を呼び出します.
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait
同時に、queueで現在どのようなOperationがあるか、およびOperationの数を取得できます.
- (NSArray *)operations;
- (NSUInteger)operationCount;
queueの優先度を設定することもできます.
- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;
記憶しやすい名前をqueueに追加します.
- (void)setName:(NSString *)n ;
- (NSString *)name ;
一時停止queue:
- (void)setSuspended:(BOOL)b;
- (BOOL)isSuspended;
2.2 NSInvocationOperation
名前の通り、この操作はNSObjectオブジェクトでメソッド(method)を呼び出すことです.同じように、同期実行の状況を見てみましょう.
self.simpleOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(simpleOperationEntry:) object:simpleObject];
[self.simpleOperation start];
これによりsimpleOperationは、作成された現在のスレッド(一般的にmain thread)で実行され、その実行は現在のスレッドを塞ぐ.
もちろん、それほど実用的な意味はありませんが、queueにsimpleOperationを追加すると、現在のスレッドと並列に実行でき、効率が向上します.
NSNumber *firstNumber = [NSNumber numberWithInteger:111];
NSNumber *secondNumber = [NSNumber numberWithInteger:222];
self.firstOperation =[[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(firstOperationEntry:) object:firstNumber];//first
self.secondOperation = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(secondOperationEntry:) object:secondNumber];//second
self.operationQueue = [[NSOperationQueue alloc] init];
/* Add the operations to the queue */
[self.operationQueue addOperation:self.firstOperation];
[self.operationQueue addOperation:self.secondOperation];
上記のコードでは,2つのOperationはそれぞれのスレッドでmain threadと並列に実行され,この2つのOperationの間でも並列に実行される.queueでget、setの最大並列Operation数を指定できます.
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
では、secondOperationをfirstOperationの後に実行させるとしたら?つまりsecondOperationはfirstOperationの実行が完了するまで待たなければならない.1つのOperationは、依存するOperationの実行が完了してから実行されるのを待つ別のOperationsまたは複数のOperationsに依存することができる.Operationに依存を追加していない場合は、いつ実行するかに制御権がありません.
addDependency:メソッドは、queueに追加する前に2つのOperationに依存属性を追加する問題を解決します.
[self.secondOperation addDependency:self.firstOperation];
依存が不要になった場合はremoveDependency:依存を解除します.
[self.secondOperation removeDependency:self.firstOperation];
また、NSInvocationOperationは、指定した初期化関数で初期化することもできます.
- (id)initWithInvocation:(NSInvocation *)inv;//designated initializer
2.3操作その他注意すべき点
1つのOperationには、実行プロセスを識別するための適切なステータスがあります.
- (BOOL)isCancelled;
- (BOOL)isExecuting;
- (BOOL)isFinished;
- (BOOL)isConcurrent;
- (BOOL)isReady;
-(void)cancelでOperationをキャンセルできます.
queueでは、-(void)cancelAllOperationsですべてのOperationをキャンセルします.