これは興味深い質問であり、その答えはすべて、どのようNSOperation
にNSURLConnection
相互作用し、連携するかというセマンティクスに関するものです。
AnNSURLConnection
自体が非同期タスクです。これはすべてバックグラウンドで行われ、そのデリゲートを定期的に呼び出して結果を返します。を開始するNSURLConnection
と、スケジュールされている実行ループを使用してデリゲート コールバックがスケジュールされるため、実行中のスレッドで実行ループが常に実行されている必要がありますNSURLConnection
。
したがって、-start
私たちのメソッドはAFURLConnectionOperation
、コールバックを受け取ることができるように、操作が終了する前に常に戻る必要があります。これにはAFURLConnectionOperation
、非同期操作である必要があります。
から: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOperation_class/index.html
プロパティの値は、現在のスレッドに対して非同期で実行される操作の場合は YES であり、現在のスレッドで同期的に実行される操作の場合は NO です。このプロパティのデフォルト値は NO です。
しかし、AFURLConnectionOperation
このメソッドをオーバーライドして、YES
期待どおりに返します。次に、クラスの説明から次のことがわかります。
非同期操作の start メソッドを呼び出すと、対応するタスクが完了する前にそのメソッドが戻る場合があります。非同期操作オブジェクトは、別のスレッドでそのタスクをスケジュールする責任があります。この操作では、新しいスレッドを直接開始するか、非同期メソッドを呼び出すか、実行のためにブロックをディスパッチ キューに送信することで、これを行うことができます。制御が呼び出し元に戻ったときに操作が進行中かどうかは実際には問題ではなく、進行中の可能性があるということだけです。
AFNetworking は、すべてのNSURLConnection
オブジェクト (およびその結果のデリゲート コールバック) をスケジュールするクラス メソッドを使用して、単一のネットワーク スレッドを作成します。これがそのコードですAFURLConnectionOperation
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
以下は、すべての実行ループ モードで AFNetwokring スレッドの実行ループをAFURLConnectionOperation
スケジュールすることを示すコードです。NSURLConnection
- (void)start {
[self.lock lock];
if ([self isCancelled]) {
[self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
} else if ([self isReady]) {
self.state = AFOperationExecutingState;
[self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
}
[self.lock unlock];
}
- (void)operationDidStart {
[self.lock lock];
if (![self isCancelled]) {
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
}
//...
}
[self.lock unlock];
}
ここでは、メソッドがそのスレッドから呼び出されるの[NSRunloop currentRunloop]
ではなく、AFNetworking スレッドで実行ループを取得します。おまけとして、バックグラウンド スレッドの実行ループでも実行できます。mainRunloop
-operationDidStart
outputStream
AFURLConnectionOperation
コールバックを待機し、ネットワーク リクエストの進行に合わせて自身の状態変数 ( 、、 )NSURLConnection
を更新します。AFNetworking スレッドはその実行ループを繰り返しスピンするため、潜在的に多くのスケジュールからコールバックが呼び出され、オブジェクトがそれらに反応できるようになります。NSOperation
cancelled
finished
executing
NSURLConnections
AFURLConnectionOperations
AFURLConnectionOperation
常にキューを使用して操作を実行する場合は、それらを同期として定義する方が簡単です。ただし、操作を手動で実行する場合は、操作オブジェクトを非同期として定義することをお勧めします。タスクの進行中の状態を監視し、KVO 通知を使用してその状態の変化を報告する必要があるため、非同期操作の定義にはより多くの作業が必要です。ただし、非同期操作を定義すると、手動で実行された操作が呼び出し元のスレッドをブロックしないようにする場合に役立ちます。
また、返されるまで呼び出して観察することによりNSOperation
、 a なしでan を使用することもできることに注意してください。同期操作として実装され、終了を待っている現在のスレッドがブロックされた場合、現在の実行ループでコールバックをスケジュールするように実際に終了することはありません。したがって、この有効な使用シナリオをサポートするには、非同期にする必要があります。NSOperationQueue
-start
-isFinished
YES
AFURLConnectionOperation
NSURLConnection
NSURLConnection
NSOperation
AFURLConnectionOperation
質問への回答
はい。AFNetworking は、すべての接続をスケジュールするために使用するスレッドを 1 つ作成します。スレッドの作成にはコストがかかります。(これが GCD が作成された理由の一部です。GCD は実行中のスレッドのプールを保持し、必要に応じて別のスレッドにブロックをディスパッチします。スレッドを自分で作成、破棄、管理する必要はありません)
バックグラウンドの AFNetworking スレッドでは処理は行われません。AFNetworking は のcompletionBlock
プロパティを使用して、が に設定されているNSOperation
ときに実行される処理を行います。finished
YES
完了ブロックの正確な実行コンテキストは保証されていませんが、通常はセカンダリ スレッドです。したがって、このブロックを使用して、非常に特殊な実行コンテキストを必要とする作業を行うべきではありません。代わりに、その作業をアプリケーションのメイン スレッドまたはそれを実行できる特定のスレッドにシャントする必要があります。たとえば、操作の完了を調整するためのカスタム スレッドがある場合、完了ブロックを使用してそのスレッドに ping を実行できます。
HTTP 接続の後処理は で処理されAFHTTPRequestOperation
ます。このクラスは、特に応答オブジェクトをバックグラウンドで変換するためのディスパッチ キューを作成し、作業をそのキューに振り分けます。ここを参照
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
self.completionBlock = ^{
//...
dispatch_async(http_request_operation_processing_queue(), ^{
//...
AFURLConnectionOperation
これは、彼らがスレッドを作成しないように書いたのではないかという疑問を投げかけていると思います. このAPIがあるので、答えはイエスだと思います
- (void)setDelegateQueue:(NSOperationQueue*) queue NS_AVAILABLE(10_7, 5_0);
これは、実行ループを使用するのではなく、特定の操作キューでデリゲート コールバックをスケジュールするためのものです。しかし、AFNetworking の古い部分を見ていると、その API は iOS 5 と OS X 10.7 でしか利用できませんでした。Github の Blame ビューを見ると、2011 年に iPhone 4s と iOS 5 が発表された実際の日に、偶然にAFURLRequestOperation
も Mattt が実際にメソッドを作成したことがわかります。スレッドが作成された時点で、非同期サブクラスで実行中にバックグラウンドで+networkRequestThread
コールバックを受信する唯一の方法は、スレッドを作成し、スレッド上で接続をスケジュールすることであったため、スレッドが存在すると推論できます。NSURLConnection
NSOperation
関数を使用してスレッドが作成されdispatch_once
ます。(あなたが提案したように追加された余分なコードを参照してください)この関数は、実行するブロックに含まれるコードがアプリケーションの存続期間中に1回だけ実行されるようにします。AFNetworking スレッドは、必要なときに作成され、アプリケーションの存続期間中存続します。
私が書いたとき、私はNSURLConnectionOperation
意味しAFURLConnectionOperation
ました。私はそれを言及してくれてありがとう:)