我想要实现的是发起一个网络请求并等待其完成,以便我可以决定应用程序下一步该做什么。
通常情况下,我会避免这样的解决方案,但这是一个罕见的情况,代码库中有很多旧的内容,我们没有足够的时间来进行必要的更改以“把事情做对”。
我正在尝试编写一个具有以下定义的简单输入输出方法:
一切都编译和运行正常,但我的用户界面(UI)会冻结,我实际上从未收到信号量(semaphore)的信号。 因此,我开始尝试使用
看起来我可能遇到了代码执行位置的问题,但我不知道如何解决并获得预期的结果。当我尝试将所有内容包装在
我错过了什么吗?这是否可以实现,而无需使用某些
我使用以下解决方案实现了我想要的功能,但它确实感觉像一种hacky的方法,不是我想在我的代码库中使用的东西。
- (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)会冻结,我实际上从未收到信号量(semaphore)的信号。 因此,我开始尝试使用
dispatch_group_t
,但结果完全相同。看起来我可能遇到了代码执行位置的问题,但我不知道如何解决并获得预期的结果。当我尝试将所有内容包装在
dispatch_async
内部时,我实际上停止了阻塞主队列,但dispatch_async
立即返回,因此在网络请求完成之前,我就从这个方法中return
了。我错过了什么吗?这是否可以实现,而无需使用某些
while
技巧?还是说我正在与风车战斗?
我使用以下解决方案实现了我想要的功能,但它确实感觉像一种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];
}