7

リクエストを非同期GCDに送信するために使用しています。HTTP動作しないコードは次のとおりです。

dispatch_async(connectionQueue, ^{
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];

        [request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]];


        NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
        [connection start];//Not working
    }); 

上記のコードはまったく機能しません。NSURLConnectionDelegate のメソッドでコールバックがありません。

しかし、次のコードを試したところ、すべてが正常に機能し、適切なコールバックが得られました

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];

[request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]];

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

dispatch_async(connectionQueue, ^{

    [connection start]; // working fine. But WHY ????
});

ブロック/GCDのこの奇妙な動作を説明してもらえますか?

4

4 に答える 4

2

非同期通信に NSURLConnection を使用する場合、そのスレッドが同じ接続のデリゲート メソッドをポーリングするようにするには、インスタンス化されたスレッドに接続をその RunLoop にアタッチする必要があります。

メイン スレッドの RunLoop に依存せずに NSURLConnection を非同期的にインスタンス化する適切な方法は次のとおりです。

// Done within a Grand Central Dispatch block, or NSOperation.

// We do not start the connection here because we still need to attach the connection to the RunLoop of the current thread and handle how it will communicate responses back to the caller.   
theConnection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];

// A previously instantiated NSOperationQueue for managing the delegate callbacks if-and-when they occur.
// [theConnection setDelegateQueue:delegateQueue];
/*
// Other NSURLConnection logic, etc.
*/    
// We start the connection manually after properly establishing how it will poll and respond to events.
[theConnection start];

// This is for the RunLoop of the current thread. (Only needed for < iOS 6 compatibility)
// If this method is executed inside a GCD block or NSOperation, it will be the RunLoop of the thread run in-parallel to the Main Thread.
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:30.0]];
// From here, you can alter the time interval to coincide with a specific "time-out" event you'd like to occur.

「theConnection」は、タイプ「NSURLConnection」の現在のクラスのメンバー変数です。また、接続が応答を受信したら、デリゲート コールバックを管理するために NSOperationQueue メンバー変数を作成する必要があります。これらの呼び出しは、接続を実行しているスレッドに非同期で通信されます。

そこから、適切な NSURLConnection デリゲート メソッドを使用してデータを返すことができます。

スレッドに Grand Central Dispatch または Operation Queues を使用する利点は、Threading および RunLoop メカニズムが既に組み込まれていることです。内部に独自の RunLoop を持つ追加のスレッドを手動で割り当てる必要はありません。これにより、バックグラウンド スレッドを作成して非同期サーバー呼び出しを管理するという 2 段階の冗長性がなくなります。

アプリケーション用の真の非同期ネットワーク モデルを作成するための正しい道筋を示すには、これで十分だと思います。:)

于 2013-05-07T17:39:22.833 に答える
2

コードサンプルの最初の部分でこれを試してください-

dispatch_async(dispatch_get_main_queue(), ^(void){
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [connection start];
}

接続をバッ​​クグラウンド キューに入れると、キューの完了後に押しのけられるため、デリゲート コールバックを取得できません。接続はメイン キューに入れることができるため、コールバックが発生するまでメインの実行ループにとどまります。または、他の人が提案したように、バックグラウンド操作を処理する独自の実行ループを作成できます。

于 2012-10-25T19:35:09.410 に答える
1

NSURLConnection は、それが作成されたスレッド (alloc init) で常にデータのフェッチを実行します。これは、2 番目の方法で機能する理由を説明しています。最初の方法は機能しますが、NSURLConnection から情報を受け取る前にスレッドが終了します。NSURLConnection はすでに非同期ダウンロードを許可していますが、データの処理を非同期で実行したい場合でも、次の方法を使用する必要があります。

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

認証が制限されているなど、その方法にはいくつかの制限があり、現在までにダウンロードされたドキュメントの量を追跡できません。作成した NSOperationQueue も指定する必要があります。デフォルト キューはメイン ループ キューです。

于 2012-10-25T19:48:07.353 に答える
1

その接続に対して一時的な NSOperationQueue を開始できます。このキューは、接続が必要な間のみ存続します。基本的に、NSOperationQueue は、デリゲート コールバックがキューに入れられ、スレッドをスピンして各デリゲート コールバックを処理することによって処理されるようにします。(ほとんどの場合、新しいデータがダウンロードされたとき、または接続が失敗したとき、接続がロードを終了したときなどに、スリープ状態になって再開されるのは同じバックグラウンド スレッドです)。このキューのセットアップが完了すると、デリゲート コールバックがアプリケーションに着信し始めます。

dispatch_async(connectionQueue, ^{
    connection = [[NSURLConnection alloc] initWithRequest:request delegate:self     startImmediately:NO];
    NSOperationQueue __autoreleasing *tempQueue = [[NSOperationQueue alloc] init];
    [connection setDelegateQueue:tempQueue];
    [connection start];
});

代わりに RunLoop を選択した場合、runloop の管理はユーザーにとって余分な負担になります。

于 2014-07-18T22:15:54.393 に答える