除了其他答案中详尽涵盖的信号量技术之外,我们现在还可以使用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
;而且,在这些测试套件中,我们必须确保直到异步调用完成后测试才能完成)。这是少数情况下可能需要使用信号量技术来阻塞主线程的情况之一。
因此,我对这个最初的问题的作者表示歉意,对于谁来说信号灯技术是健全的,我向所有看到这种信号灯技术并考虑在其代码中应用它作为处理异步的通用方法的所有新开发人员写此警告。方法:警告:十分之九,信号量技术是 不是遇到异步操作时最好的方法。相反,您应该熟悉完成阻止/关闭模式,委托协议模式和通知。这些通常是处理异步任务的更好方法,而不是使用信号量使它们同步运行。通常,将异步任务设计为异步行为是有充分理由的,因此请使用正确的异步模式,而不是尝试使其同步行为。
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
用while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]; }