2012-03-06 59 views
6

我正在创建一个从数据库中获取一组结果的应用程序 - 我使用MBProgressHUD来显示查询的动画进度。我使用的方法在另一个线程中执行方法时调用动画,一旦完成,它就隐藏动画。我的问题是,打完电话后:iOS中的多线程 - 如何强制线程等待条件?

[HUD showWhileExecuting:@selector(getResults) onTarget:self withObject:nil animated:YES]; 

我想,如果没有结果,显示一个警告,说明这一点,如果有,加载下一个视图。到目前为止,我有这样的代码:

[HUD showWhileExecuting:@selector(getResults) onTarget:self withObject:nil animated:YES]; 

if(self.thereAreEvents) { 
    [self performSegueWithIdentifier:@"searchResults" sender:self]; 
} else { 
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No results" message:@"Sorry, there are no results for your search. Please try again." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; 
    [alert show]; 
    [alert release]; 
} 

self.thereAreEvents被设定在getResults方法结束。但是,由于该方法在另一个线程中被调用,因此即使数据库中存在事件,该行的执行也会继续并显示警报。

所以,从这里开始,我有两个问题:什么是在iOS中实现等待信号机制的最简单方法,以及在iOS中实现这种机制的最有效方式是什么?

谢谢!

回答

2

你也可以考虑一个NSConditionLock

所以它会是在线程1是这样的:

[conditionLock lockWhenCondition:kConditionOkayToProceed]; 
[conditionLock unlockWithCondition:kConditionGettingResults]; 

[HUD show...] 

[conditionLock lockWhenCondition:kConditionResultsFetched]; 
[conditionLock unlockWithCondition:kConditionOkayToProceed]; 

而在HUD:

- (void)show... 
{ 
    [conditionLock lockWhenCondition:kConditionGettingResults]; 

    // stuff here 

    [conditionLock unlockWithCondition:kConditionResultsFetched]; 
} 

虽然一个更好的解决办法是通过一个块或目标/选择器在选取结果时将执行的HUD。

编辑:所以你会最终像代码:

[HUD showWhileExecuting:@selector(getResults) 
    onTarget:self 
    withObject:nil 
    animated:YES 
    performWhenFinished: 
    ^{ 
     if(self.thereAreEvents) { 
      [self performSegueWithIdentifier:@"searchResults" sender:self]; 
     } else { 
      UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No results" message:@"Sorry, there are no results for your search. Please try again." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; 
      [alert show]; 
      [alert release]; 
     } 
     }]; 

而在HUD:

- (void)showWhile... performWhenFinished:(dispatch_block_t)block 
{ 
    // all the other stuff you were going to do here, then 
    // eventually... 

    // if no guarantees, maybe just: 
    block(); 

    // otherwise, if promised to dispatch to the main queue: 
    dispatch_async(dispatch_get_main_queue(), block); 
} 

采用具有额外的智能HUD采取dispatch_block_t作为最后一个参数和在结果存在时调用它(无论是否保证调度回到主线程或其他方式)。

+0

。我仍然需要学习iOS编程。我会看看那个。谢谢! – KerrM 2012-03-06 19:46:19

+0

'通过一个区块'模式是相对较新的,但似乎是苹果公司在iOS 5中的新API中领导的方向。 'TWRequest'或'ACAccountStore'。实际上,按照似乎正在出现的惯例,使用'completionHandler:'而不是'performWhenFinished:'来提供块可能会更好。 – Tommy 2012-03-06 19:55:00

0

不知道这是否是最好的办法,但我会尝试使用while循环来观察一些布尔类型,如[NSThread sleepForTimeInterval:1];在循环内。

也许设置超时。

0

这是你的方式: Concurrency Programming Guide

另外:Synchronization

更加犀利:Using Locks。我认为最后一个可以提供最好的帮助。

另一种简单的方法,这是不好的,但工程

NSAssert(![NSThread isMainThread], @"Do not run on MAIN thread"); 
while (yourCondition) { [NSThread sleepForTimeInterval:0.2]; } 
11

您可以使用一个忙等循环的快速和肮脏的解决方案:

__block BOOL finished = NO; 
dispatch_async(/* global queue */, ^{ 
    // … 
    finished = YES; 
}); 
while (!finished) /* waiting */; 

在“真实”的代码,最好使用信号量:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 
dispatch_async(/* global queue */, ^{ 
    // … 
    dispatch_semaphore_signal(semaphore); 
}); 
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 
dispatch_release(sempahore); 

这比忙循环更好,因为被阻塞的线程不会占用CPU时间。

最好的解决方案是阻止并重新设计您的代码以异步工作。在你的情况下,你应该显示一个微调并开始下载数据。数据完成下载后,您应该收到异步回调(通过块或目标/操作回调)并显示结果或显示错误警报。在这种情况下,使用繁忙循环或信号量阻塞是一个穷人的解决方案。

+0

感谢您的答复。诚实地说,在编程iOS时我完全失去了,但我会研究异步回调。再次感谢! – KerrM 2012-03-06 19:47:57

+0

只是一个评论,dispatch_release不需要,因为我看到iOS6 – 2016-03-11 19:36:55

0

可以使用dispatch_time_t作为..

double delayInSeconds = 2.0; 
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
    <#code to be executed on the main queue after delay#> 
});