Окрім техніки семафору, вичерпно висвітленої в інших відповідях, тепер ми можемо використовувати XCTest у Xcode 6 для виконання асинхронних тестів через 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
, наприклад, у цих наборах тестування ми повинні переконатися, що тест не закінчується, поки не буде виконано асинхронний виклик). Це одна з тих рідкісних ситуацій, коли може бути необхідна техніка семафору для блокування головної нитки.
Тож, вибачаючись перед автором цього оригінального питання, для якого звучить техніка семафору, я пишу це попередження всім тим новим розробникам, які бачать цю семафорну техніку і розглядають застосування її у своєму коді як загальний підхід для роботи з асинхронною методи: Попереджуйте, що дев'ять разів з десяти є семафорною технікою не такнайкращий підхід при підрахунку асинхронних операцій. Натомість ознайомтеся з блоками / шаблонами закриття, а також з моделями протоколів делегатів та сповіщеннями. Це часто набагато кращі способи вирішення асинхронних завдань, ніж використання семафорів, щоб змусити їх поводитися синхронно. Зазвичай є вагомі причини того, що асинхронні завдання покликані поводитись асинхронно, тому використовуйте правильний асинхронний зразок, а не намагайтеся змусити їх поводитись синхронно.
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
зwhile (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]; }