アプリで一連のダウンロード操作とデータベース書き込み操作を実行する必要があります。同じためにNSOperation
とNSOperationQueue
を使用しています。
これはアプリケーションシナリオです。
前述のように、各タスクに対してNSOperation
を定義しました。最初のケース(Task1)では、すべての郵便番号を取得するリクエストをサーバーに送信しています。 NSOperation
内のデリゲートはデータを受け取ります。その後、このデータはデータベースに書き込まれます。データベース操作は別のクラスで定義されています。 NSOperation
クラスから、データベースクラスで定義された書き込み関数を呼び出しています。
私の質問は、データベースの書き込み操作がメインスレッドで発生するか、バックグラウンドスレッドで発生するかです。 NSOperation
内で呼び出していたので、NSOperation
とは異なるスレッド(MainThreadではない)で実行されることを期待していました。誰かがNSOperation
とNSOperationQueue
を扱っている間にこのシナリオを説明してもらえますか。
私の質問は、データベースの書き込み操作がメインスレッドで発生するか、バックグラウンドスレッドで発生するかです。
次のようにゼロからNSOperationQueue
を作成する場合:
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
バックグラウンドスレッドになります。
通常、操作キューは、操作の実行に使用されるスレッドを提供します。 OS X v10.6以降では、操作キューはlibdispatchライブラリ(Grand Central Dispatchとも呼ばれます)を使用して操作の実行を開始します。 結果として、操作は同時操作または非同時操作として指定されているかどうかに関係なく、常に個別のスレッドで実行されます
mainQueue
を使用している場合を除き:
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
次のようなコードも表示できます。
NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
[myQueue addOperationWithBlock:^{
// Background work
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// Main thread work (UI usually)
}];
}];
GCDバージョン:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
{
// Background work
dispatch_async(dispatch_get_main_queue(), ^(void)
{
// Main thread work (UI usually)
});
});
NSOperationQueue
は、あなたがやりたいことをきめ細かく制御します。 2つの操作の間に依存関係を作成できます(ダウンロードしてデータベースに保存します)。あるブロックと他のブロックの間でデータを渡すために、たとえば、サーバーからNSData
が来ると想定できます。
__block NSData *dataFromServer = nil;
NSBlockOperation *downloadOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakDownloadOperation = downloadOperation;
[weakDownloadOperation addExecutionBlock:^{
// Download your stuff
// Finally put it on the right place:
dataFromServer = ....
}];
NSBlockOperation *saveToDataBaseOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakSaveToDataBaseOperation = saveToDataBaseOperation;
[weakSaveToDataBaseOperation addExecutionBlock:^{
// Work with your NSData instance
// Save your stuff
}];
[saveToDataBaseOperation addDependency:downloadOperation];
[myQueue addOperation:saveToDataBaseOperation];
[myQueue addOperation:downloadOperation];
編集:操作に__weak
参照を使用している理由は、 こちら にあります。しかし、簡単に言えば、保持サイクルを回避することです。
バックグラウンドスレッドでデータベース書き込み操作を実行する場合は、そのスレッドのNSManagedObjectContext
を作成する必要があります。
関連するNSManagedObjectContext
サブクラスのstartメソッドで背景NSOperation
を作成できます。
Appleのドキュメントで、 コアデータとの同時実行性を確認してください。
NSManagedObjectContext
でリクエストを作成し、performBlock:
メソッド内でリクエストを実行することにより、独自のバックグラウンドスレッドでリクエストを実行するNSPrivateQueueConcurrencyType
を作成することもできます。
IOS 4以降では、操作キューはGrand Central Dispatchを使用して操作を実行します。 iOS 4より前は、非並行操作用に個別のスレッドを作成し、現在のスレッドから並行操作を起動します。
そう、
[NSOperationQueue mainQueue] // added operations execute on main thread
[NSOperationQueue new] // post-iOS4, guaranteed to be not the main thread
あなたの場合、NSThread
をサブクラス化して独自の「データベーススレッド」を作成し、performSelector:onThread:
を使用してメッセージを送信することができます。
NSOperationの実行スレッドは、操作を追加したNSOperationQueue
に依存します。コードでこのステートメントを見てください-
[[NSOperationQueue mainQueue] addOperation:yourOperation]; // or any other similar add method of NSOperationQueue class
これはすべて、main
のNSOperation
メソッドでこれ以上スレッドを実行していないことを前提としています。
ただし、同時操作の場合、シナリオは異なります。キューは、同時操作ごとにスレッドを生成する場合があります。保証されていませんが、システムリソースと操作リソースの要求に依存しますその時点でシステム内。 maxConcurrentOperationCount
プロパティにより、操作キューの同時実行性を制御できます。
編集-
私はあなたの質問に興味を持ち、自分で分析/記録を行いました。 NSOperationQueueはメインスレッドで次のように作成されています-
self.queueSendMessageOperation = [[[NSOperationQueue alloc] init] autorelease];
NSLog(@"Operation queue creation. current thread = %@ \n main thread = %@", [NSThread currentThread], [NSThread mainThread]);
self.queueSendMessageOperation.maxConcurrentOperationCount = 1; // restrict concurrency
次に、NSOperationを作成し、addOperationを使用して追加しました。現在のスレッドをチェックしたときのこの操作のメインメソッドでは、
NSLog(@"Operation obj = %@\n current thread = %@ \n main thread = %@", self, [NSThread currentThread], [NSThread mainThread]);
メインスレッドとしてではありませんでした。そして、現在のスレッドオブジェクトがメインスレッドオブジェクトではないことがわかりました。
したがって、メインスレッドでのキューのカスタム作成(操作間の同時実行性なし)は、操作がメインスレッド自体でシリアルに実行されることを必ずしも意味しません。
ドキュメントの要約はoperations are always executed on a separate thread
です(iOS 4がGCDの基礎となる操作キューを意味する後)。
メインスレッド以外で実際に実行されているかどうかを確認するのは簡単です。
NSLog(@"main thread? %@", [NSThread isMainThread] ? @"YES" : @"NO");
スレッドで実行する場合、GCD/libdispatchを使用して、メインスレッドで何かを実行するのは簡単です。コアデータ、ユーザーインターフェイス、またはメインスレッドでの実行に必要なその他のコード:
dispatch_async(dispatch_get_main_queue(), ^{
// this is now running on the main thread
});