0

メインスレッドと対話せずに、バックグラウンドでいくつかの処理を行う必要がある静的ライブラリを開発しています。アイデアを得るために、いくつかのユーザー イベントをログに記録することを考えてみてください。ライブラリは、ユーザーがアプリを終了するか、バックグラウンドに送信する (ホーム ボタンを押す) まで、この処理を実行し続ける必要があります。つまり、ループ内で処理を実行し続ける必要があります。

メイン アプリ スレッドと生成されたスレッドの間の唯一の相互作用は、メイン アプリ スレッドが、生成されたスレッドが読み取り/消費できるキューにいくつかのもの (イベント オブジェクト) を入れる場合があることです。それ以外では、生成されたスレッドは、アプリが存在するかバックグラウンドになるまで続きます。

生成されたスレッドが実行する必要があることの一部 (すべてではありません) には、HTTP サーバーへのデータの送信が含まれます。NSThread をサブクラス化し、そのメイン メソッドをオーバーライドし、その接続である種のタイムアウトを指定して NSUrlConnection への同期呼び出しを行うだけで、スレッドが永久にハングしないようにするのは簡単だと思いました。たとえば、Java/Android では、Thread をサブクラス化し、start() メソッドをオーバーライドして、同期 HTTP GET メソッドを呼び出します (たとえば、Apache の HttpClient クラスから)。これは非常に簡単で、うまく機能します。しかし、私がここや他の場所で見たものから、どうやらiOSではこれよりもはるかに複雑であり、実際に機能する最良のアプローチは何かについて少し混乱しています.

では、NSThread をサブクラス化し、何らかの方法で NSUrlConnection を使用する必要がありますか? デリゲート メソッドが呼び出されないため、NSThread 内で非同期 NSUrlConnection が機能しないようですが、同期メソッドはどうですか? RunLoop を使用して構成し、自動解放プールを設定する必要がありますか? または、NSOperation を使用する必要がありますか? 私がやろうとしていることはかなり一般的であるように思えます - 誰かがこれを適切に行う方法の実例を持っていますか?

4

2 に答える 2

1

私が理解しているように、NSURLConnection非同期で使用するにはランループが必要です。を使用しNSOperationても、実行ループが必要です。

私が見たすべての例は、実行ループを持つMain Thread開始に を使用しNSURLConnectionています。使用する例は、操作が独自のスレッドを提供しないNSOperationように設定されているため、たとえばへの呼び出しを介して、メインスレッドで開始されることを確認します。ConcurrentNSOperationQueueNSURLConnectionperformSelectorOnMainThread:

次に例を示します。

Pulse Engineering ブログ: NSOperationQueues を使用した同時ダウンロード

QRunLoopOperationまた、Apple のドキュメントでサンプルを検索することもできます。このLinkedImageFetcherサンプルは、この種の詳細を示すクラスの例です。

(独自の実行ループを実行する方法を示す例のコードを実際に見たかどうかはわかりませんが、この例もメイン スレッドに依存しています。)

于 2013-04-04T11:12:43.660 に答える
0

これを実現するために、Grand Central Dispatch (GCD) メソッドを使用しました。これは、単純なテストアプリで私のために働いた例です (静的ライブラリに適用されるかどうかはわかりませんが、一見の価値があるかもしれません)。ARCを使用しています。

この例では、viewDidLoad メソッドからいくつかのバックグラウンド作業を開始していますが、どこからでも開始できます。重要なのは、「dispatch_async(dispatch_get_global_queue…」がバックグラウンド スレッドでブロックを実行することです。その方法の適切な説明については、この回答を参照してください。

これが私のviewDidLoadです:

- (void)viewDidLoad
{
    [super viewDidLoad];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), 
        ^(void) {
            [self doStuffInBackground];
        });
}

この時点で doStuffInBackground メソッドはバックグラウンドで実行されているため、NSURLConnection を同期的に使用できます。ここでの私の例では、おそらく他のコードが backgroundStuffShouldRun = false を設定するまで、メソッドはネットワーク呼び出しをループします。ネットワーク呼び出しは、10 秒のタイムアウトで行われます。通話の後、進行状況を表示するためだけに UI ラベルを更新しています。UI の更新は "dispatch_async(dispatch_get_main_queue()…") で実行されることに注意してください。これにより、必要に応じて UI スレッドで UI の更新が実行されます。

このバックグラウンド作業の 1 つの潜在的な問題: http 要求自体をキャンセルする方法がありません。ただし、タイムアウトが 10 秒の場合、部外者 (おそらく UI の何らかのイベント) が backgroundStuffShouldRun = false を設定した後、スレッドがそれ自体を中止するまで最大 10 秒待機することになります。

- (void)doStuffInBackground
{
    while (backgroundStuffShouldRun) {
        // prepare for network call...
        NSURL* url = [[NSURL alloc] initWithString:@"http://maps.google.com/maps/geo"];

        // set a 10 second timeout on the request
        NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLCacheStorageAllowed timeoutInterval:10];

        NSError* error = nil;
        NSURLResponse *response = nil;

        // make the request
        NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

        // were we asked to stop the background processing?
        if (!backgroundStuffShouldRun) {
            return;
        }

        // process response...

        NSString* status = @"Success";

        if (error) {
            if (error.code == NSURLErrorTimedOut) {
                // handle timeout...
                status = @"Timed out";
            }
            else {
                // handle other errors...
                status = @"Other error";
            }
        }
        else {
            // success, handle the response body
            NSString *dataAsString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"%@", dataAsString);
        }


        // update the UI with our status
        dispatch_async(dispatch_get_main_queue(), ^{
            [statusLabel setText:[NSString stringWithFormat:@"completed network call %d, status = %@", callCount, status]];
        });

        callCount++;
        sleep(1); // 1 second breather. not necessary, but good idea for testing
    }

}
于 2013-04-10T01:08:18.147 に答える