Oprócz techniki semaforów omówionej wyczerpująco w innych odpowiedziach, możemy teraz używać XCTest w Xcode 6 do wykonywania testów asynchronicznych za pośrednictwem XCTestExpectation
. Eliminuje to konieczność stosowania semaforów podczas testowania kodu asynchronicznego. Na przykład:
- (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];
}
Ze względu na przyszłych czytelników, chociaż technika semafora wysyłki jest cudowną techniką, gdy jest absolutnie potrzebna, muszę przyznać, że widzę zbyt wielu nowych programistów, niezaznajomionych z dobrymi wzorcami programowania asynchronicznego, zbyt szybko przechodzą na semafory jako ogólny mechanizm tworzenia asynchronicznych procedury zachowują się synchronicznie. Co gorsza, wielu z nich korzysta z tej techniki semaforów z głównej kolejki (i nigdy nie powinniśmy blokować głównej kolejki w aplikacjach produkcyjnych).
Wiem, że tak nie jest w tym przypadku (kiedy to pytanie zostało opublikowane, nie było takiego fajnego narzędzia XCTestExpectation
; również w tych zestawach testowych musimy upewnić się, że test nie zakończy się, dopóki nie zostanie wykonane wywołanie asynchroniczne). Jest to jedna z tych rzadkich sytuacji, w których może być konieczna technika semaforów do blokowania głównego wątku.
Dlatego przepraszam autora tego oryginalnego pytania, dla którego technika semafora jest dobra, piszę to ostrzeżenie do wszystkich nowych programistów, którzy widzą tę technikę semaforów i rozważają zastosowanie jej w kodzie jako ogólnego podejścia do radzenia sobie z asynchronicznym metody: Ostrzegamy, że dziewięć razy na dziesięć technika semaforów jest nienajlepsze podejście w przypadku operacji asynchronicznych. Zamiast tego zapoznaj się z wzorcami blokowania / zamykania, a także wzorcami i powiadomieniami protokołu delegowania. Są to często znacznie lepsze sposoby radzenia sobie z zadaniami asynchronicznymi, niż używanie semaforów, aby zachowywały się synchronicznie. Zwykle istnieją dobre powody, dla których zadania asynchroniczne zostały zaprojektowane tak, aby zachowywały się asynchronicznie, więc należy stosować właściwy wzorzec asynchroniczny zamiast próbować je synchronizować.
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
zwhile (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]; }