2012-10-25 18 views
7

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

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のメソッドでコールバックを取得していません。私は次のコードをしようとしたとき

はしかし、すべてが正常に働いていたと私はいくつかのいずれかがブロック/ GCDのこの奇妙な振る舞いを説明していただけます適切なコールバック

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 ???? 
}); 

を得ましたか。

+1

NSURLConnectionはすでに非同期データロードを行っていますが、なぜGCDを使用するのですか? Appleが推奨するのは –

+1

ですか? GCDを使用してコードの上にロックを維持します。 – Ashu

+0

2番目のサンプルは、dispatch_asyncブロックの前にすでに接続を開始しています。 "StartsImmediately"ブール値を "NO"に設定して接続をすぐに開始しないように指示しない場合、接続はインスタンス化されるとすぐに起動します。この場合、dispatch_asyncブロックは冗長です。非同期的にNSURLConnectionを使用する方法の例については、私の答えを参照してください。 – user298261

答えて

2

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

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

あなたがバックグラウンドキューで接続を置く場合は、キューが完了した後に離れて押し込んますので、あなたが得ることはありませんあなたデリゲートコールバック。接続はメインキューにあるため、コールバックが発生するようにメイン実行ループにとどまります。または、他の人が示唆するように、自分のバックグラウンド操作を処理する独自のランループを作成することもできます。

+1

接続は絶対にメインスレッド上にあるべきではありません。テクニカルQA 1693を参照してください。https://developer.apple.com/library/ios/#qa/qa1693/_index.html – quellish

+2

QA 1693には何も書かれていません。 –

+1

少なくとも、接続が真に非同期的に実行されることになっている場合は、メインスレッドには入れないでください。ポスターは、接続イベントを適切にポーリングするためにバックグラウンドスレッドのRunLoopに正しくアクセスする方法を学ぶ必要があります。 – user298261

1

NSURLConnectionは、作成されたスレッド(alloc init)で常にデータのフェッチを実行します。これはなぜそれが第2の方法で動作するのかを説明します。最初の方法は動作しますが、NSURLConnectionから情報を受け取る前にスレッドが停止します。 NSURLConnectionはすでに非同期でダウンロードすることができますが、あなたも、非同期データの取り扱いを実行したい場合は、以下の方法で使用する必要があります。認証が限られており、あなたはどのように追跡することはできませんようにそのメソッドを持ついくつかの制限があります

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

をドキュメントの多くがダウンロードされています。作成するNSOperationQueueも指定する必要があります。デフォルトのキューはメインループキューです。

2

NSURLConnectionを非同期通信に使用する場合、インスタンス化されたスレッドに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を使用する利点は、スレッド化とRunLoop機構がすでに組み込まれていることです。独自のRunLoopをスレッド内に手動で割り当てる必要はありません。これにより、バックグラウンドスレッドを作成して非同期サーバーコールを管理する2ステップの冗長性が排除されます。

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

+1

'setDelegateQueue'(iOS 5+)を使用すると、 scheduleInRunLoop'。 [docs](http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html#//apple_ref/doc/uid/20001697-SW3) "この[' 'setDelegateQueue']メソッドと' 'scheduleInRunLoop:forMode:'メソッドの両方でデリゲートメッセージをスケジュールするのは誤りです。 'scheduleInRunLoop'は、' NSThread'と独自のrunloopを作成する場合に使用します。 'NSOperationQueue'(これは簡単ですが、IMHO)を使うつもりなら' setDelegateQueue'を使います。 – Rob

+0

優れた観察!私はあなたが言ったことを反映するためにコードサンプルを調整します。ありがとうございました。 – user298261

+0

@Rob私はちょうどいくつかのコードを変更しました。私は、[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:30.0]]を使用して、私の電話でより速い応答を得ました。非常に興味深い... :) – user298261

1

その接続の一時NSOperationQueueを開始することができます。このキューは、接続が必要とする間だけ有効です。基本的にNSOperationQueueは、デリゲートコールバックがキューイングされ、各デリゲートコールバックを処理するためにスレッドを回転させることによって処理されることを保証します。 (ほとんどの場合、新しいデータがダウンロードされたとき、または接続が失敗したとき、接続が終了したときなどにスリープ状態に戻り、再開されるバックグラウンドスレッドと同じです)。このキューの設定が完了すると、デレゲートコールバックがアプリケーションに流入し始めます。

RunLoopを選択した場合、ランループを管理することはあなたの負担です。