【发布时间】:2019-07-25 13:05:18
【问题描述】:
我想要实现的是发出网络请求并等待它完成,以便我可以决定下一步应该是什么应用程序。 通常我会避免这样的解决方案,但这种情况很少见,因为代码库有很多遗留问题,我们没有足够的时间应用必要的更改来把事情做好。
我正在尝试使用以下定义编写一个简单的输入输出方法:
- (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 hacks,这实际上可以实现吗?还是我想和风车搏斗?
我能够通过以下解决方案实现我想要的,但它真的感觉像是一种 hacky 方式,而不是我喜欢在我的代码库中发布的东西。
- (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];
}
【问题讨论】:
-
我会推荐执行异步代码并显示一个 Hud 以等待异步任务结束,因为不推荐锁定 UI 等待这样的任务。
-
@kjoe 正如我所提到的,我对这种方法也不满意。问题是在这个阶段我不能这样做。
标签: ios objective-c concurrency grand-central-dispatch