Zusätzlich zu der Semaphorentechnik, die in anderen Antworten ausführlich behandelt wird, können wir jetzt XCTest in Xcode 6 verwenden, um asynchrone Tests über durchzuführen XCTestExpectation
. Dadurch werden beim Testen von asynchronem Code keine Semaphoren benötigt. Beispielsweise:
- (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];
}
Für zukünftige Leser ist die Versand-Semaphor-Technik zwar eine wunderbare Technik, wenn sie unbedingt benötigt wird, aber ich muss gestehen, dass zu viele neue Entwickler, die mit guten asynchronen Programmiermustern nicht vertraut sind, zu schnell zu Semaphoren als allgemeinem Mechanismus für die asynchrone Erstellung tendieren Routinen verhalten sich synchron. Schlimmer noch, ich habe gesehen, dass viele von ihnen diese Semaphor-Technik aus der Hauptwarteschlange verwenden (und wir sollten die Hauptwarteschlange in Produktions-Apps niemals blockieren).
Ich weiß, dass dies hier nicht der Fall ist (als diese Frage gestellt wurde, gab es kein nettes Tool wie XCTestExpectation
; außerdem müssen wir in diesen Testsuiten sicherstellen, dass der Test erst beendet wird, wenn der asynchrone Aufruf abgeschlossen ist). Dies ist eine der seltenen Situationen, in denen die Semaphortechnik zum Blockieren des Hauptthreads erforderlich sein kann.
Mit meiner Entschuldigung an den Autor dieser ursprünglichen Frage, für den die Semaphortechnik fundiert ist, schreibe ich diese Warnung an alle neuen Entwickler, die diese Semaphortechnik sehen und in Betracht ziehen, sie in ihrem Code als allgemeinen Ansatz für den Umgang mit asynchronem Verhalten anzuwenden Methoden: Seien Sie gewarnt, dass neun von zehn die Semaphor-Technik nicht istDer beste Ansatz bei der Erfassung asynchroner Vorgänge. Machen Sie sich stattdessen mit Abschlussblock- / Abschlussmustern sowie mit Delegiertenprotokollmustern und Benachrichtigungen vertraut. Dies sind oft viel bessere Möglichkeiten, um mit asynchronen Aufgaben umzugehen, als Semaphoren zu verwenden, damit sie sich synchron verhalten. Normalerweise gibt es gute Gründe dafür, dass asynchrone Aufgaben so konzipiert sind, dass sie sich asynchron verhalten. Verwenden Sie daher das richtige asynchrone Muster, anstatt zu versuchen, sie synchron zu verhalten.
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
mitwhile (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]; }