他の回答で網羅されているセマフォ手法に加えて、Xcode 6のXCTestを使用して、を介して非同期テストを実行できますXCTestExpectation
。これにより、非同期コードをテストするときにセマフォが不要になります。例えば:
- (void)testDataTask
{
XCTestExpectation *expectation = [self expectationWithDescription:@"asynchronous request"];
NSURL *url = [NSURL URLWithString:@"http://www.apple.com"];
NSURLSessionTask *task = [self.session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
XCTAssertNil(error, @"dataTaskWithURL error %@", error);
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *) response statusCode];
XCTAssertEqual(statusCode, 200, @"status code was not 200; was %d", statusCode);
}
XCTAssert(data, @"data nil");
// do additional tests on the contents of the `data` object here, if you want
// when all done, Fulfill the expectation
[expectation fulfill];
}];
[task resume];
[self waitForExpectationsWithTimeout:10.0 handler:nil];
}
将来の読者のために、ディスパッチセマフォ手法は絶対に必要なときに素晴らしい手法ですが、優れた非同期プログラミングパターンに不慣れな新しい開発者が多すぎて、非同期にするための一般的なメカニズムとしてセマフォにすぐに引き寄せられることを認めなければなりません。ルーチンは同期的に動作します。さらに悪いことに、それらの多くがメインキューからこのセマフォ手法を使用していることを確認しました(本番アプリではメインキューをブロックしないでください)。
私はこれが事実ではないことを知っています(この質問が投稿されたとき、のような素晴らしいツールはありませんでしたXCTestExpectation
。また、これらのテストスイートでは、非同期呼び出しが完了するまでテストが終了しないことを確認する必要があります)。これは、メインスレッドをブロックするためのセマフォテクニックが必要になるまれな状況の1つです。
したがって、セマフォ手法が適切であるこの元の質問の作成者に謝罪し、このセマフォ手法を理解しているすべての新しい開発者にこの警告を書き込み、非同期を処理するための一般的なアプローチとしてコードに適用することを検討します方法は:10のうち9回は、セマフォ技術があることをあらかじめご了承ません非同期操作に遭遇するときの最良のアプローチ。代わりに、完了ブロック/クロージャーパターン、およびデリゲートプロトコルパターンと通知をよく理解してください。これらは多くの場合、セマフォを使用して同期的に動作させるよりも、非同期タスクを処理するはるかに優れた方法です。通常、非同期タスクが非同期的に動作するように設計されていることには十分な理由があるため、同期的に動作させるのではなく、適切な非同期パターンを使用してください。
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
とwhile (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]; }