我要实现的目标是发出网络请求并等待它完成,这样我就可以决定下一步应该是什么应用。 通常,我会避免这种解决方案,但是这种情况很少见,在这种情况下,代码库有很多遗留问题,并且我们没有足够的时间来应用必要的更改以使事情变得正确。
我正在尝试编写具有以下定义的简单输入输出方法:
- (nullable id<UserPaymentCard>)validCardForLocationWithId:(ObjectId)locationId;
问题是,为了在此方法内执行某些验证,我需要发出网络请求以仅接收必要的信息,因此我想等待此请求完成。
我想到的第一件事是使用dispatch_semaphore_t
,所以我最终得到了这样的东西:
- (nullable id<UserPaymentCard>)validCardForLocationWithId:(ObjectId)locationId {
id<LocationsReader> locationsReader = [self locationsReader];
__block LocationStatus *status = nil;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[locationsReader fetchLocationProviderStatusFor:locationId completion:^(LocationStatus * _Nonnull locationStatus) {
status = locationStatus;
dispatch_semaphore_signal(sema);
} failure:nil];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
return [self.paymentCards firstCardForStatus:status];
}
所有内容都能编译并运行,但是我的UI冻结了,实际上我从未收到sempahore的信号。
因此,我开始玩dispatch_group_t
,结果完全一样。
看起来我可能在执行代码的地方遇到了一些问题,但是我不知道该如何处理并获得预期的结果。当我尝试将所有内容包装在dispatch_async
中时,我实际上停止了阻塞主队列,但是dispatch_async
立即返回,因此我在网络请求完成之前通过此方法return
。
我想念什么?如果没有一些while
骇客,实际上可以实现吗?还是我想打风车?
我可以通过以下解决方案实现我想要的,但是这确实感觉像是一种骇人听闻的方式,而不是我不希望在我的代码库中提供的东西。
- (nullable id<UserPaymentCard>)validCardForLocationWithId:(ObjectId)locationId {
id<LocationsReader> locationsReader = [self locationsReader];
__block LocationStatus *status = nil;
__block BOOL flag = NO;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[locationsReader fetchLocationProviderStatusFor:locationId completion:^(LocationStatus * _Nonnull locationStatus) {
status = locationStatus;
flag = YES;
} failure:nil];
});
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) && !flag){};
return [self.paymentCards firstCardForStatus:status];
}
答案 0 :(得分:1)
我猜fetchLocationProviderStatusFor:completion:failure:
在主队列中调用了这些回调。这就是为什么您陷入僵局。不可能。我们还不能定时旅行。
答案 1 :(得分:1)
已弃用的NSURLConnection.sendSynchronousRequest
API对于您确实无法(或无法打扰)正确执行操作的实例很有用,例如以下示例:
private func pageExists(at url: URL) -> Bool {
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
request.timeoutInterval = 10
var response: URLResponse?
try! NSURLConnection.sendSynchronousRequest(request,
returning: &response)
let httpResponse = response as! HTTPURLResponse
if httpResponse.statusCode != 200 { return false }
if httpResponse.url != url { return false }
return true
}
答案 2 :(得分:0)
当前,您的方法使工作在主线程上完成,这将冻结UI。您的解决方案可以工作,但是最好更改方法以包括完成块。然后,您可以在异步块的末尾调用完成块。这是该示例代码:
- (void)validCardForLocationWithId:(ObjectId)locationId completion:(nullable id<UserPaymentCard> (^)(void))completion {
id<LocationsReader> locationsReader = [self locationsReader];
__block LocationStatus *status = nil;
[locationsReader fetchLocationProviderStatusFor:locationId completion:^(LocationStatus * _Nonnull locationStatus) {
status = locationStatus;
completion([self.paymentCards firstCardForStatus:status]);
} failure:nil];
}