En plus de la technique du sémaphore abordée de manière exhaustive dans d'autres réponses, nous pouvons désormais utiliser XCTest dans Xcode 6 pour effectuer des tests asynchrones via XCTestExpectation
. Cela élimine le besoin de sémaphores lors du test de code asynchrone. Par exemple:
- (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");
[expectation fulfill];
}];
[task resume];
[self waitForExpectationsWithTimeout:10.0 handler:nil];
}
For the sake of future readers, while the dispatch semaphore technique is a wonderful technique when absolutely needed, I must confess that I see too many new developers, unfamiliar with good asynchronous programming patterns, gravitate too quickly to semaphores as a general mechanism for making asynchronous routines behave synchronously. Worse I've seen many of them use this semaphore technique from the main queue (and we should never block the main queue in production apps).
I know this isn't the case here (when this question was posted, there wasn't a nice tool like XCTestExpectation
; also, in these testing suites, we must ensure the test does not finish until the asynchronous call is done). This is one of those rare situations where the semaphore technique for blocking the main thread might be necessary.
So with my apologies to the author of this original question, for whom the semaphore technique is sound, I write this warning to all of those new developers who see this semaphore technique and consider applying it in their code as a general approach for dealing with asynchronous methods: Be forewarned that nine times out of ten, the semaphore technique is not the best approach when encounting asynchronous operations. Instead, familiarize yourself with completion block/closure patterns, as well as delegate-protocol patterns and notifications. These are often much better ways of dealing with asynchronous tasks, rather than using semaphores to make them behave synchronously. Usually there are good reasons that asynchronous tasks were designed to behave asynchronously, so use the right asynchronous pattern rather than trying to make them behave synchronously.
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
parwhile (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]; }