我想启动一个在另一个线程上运行的任务“以防万一需要”,以最大限度地缩短用户以后必须等待的时间。如果有时间完成,用户将不必等待,但如果尚未完成,则需要等待。
在viewDidLoad:
中打开数据库,当用户按下屏幕上的按钮时,将需要该数据库。如果我等待打开数据库,直到用户实际按下按钮,则存在延迟。所以我想早点打开它。由于我不知道打开需要多长时间,而且我不知道用户点击按钮需要多长时间,我需要一种说法,如果其他任务尚未完成,那么请等待,否则继续。
例如:
@implementation aViewController
- (void) viewDidLoad {
[self.dbManager openOrCreateDbWithCompletionHandler: ^(NSError *err) {
if( err ) NSLog( @"There was a problem opening the database" );
}];
}
- (IBAction) goButtonTouched: (id) sender {
// Wait here until the database is open and ready to use.
if( ???DatabaseNotAvailableYet??? ) {
[self putSpinnerOnScreen];
???BlockProgressHereUntilDatabaseAvailable???
[self takeSpinnerOffScreen];
}
// Use the database...
NSManagedObjectContext *context = [self theDatabaseContext];
// Build the search request for the attribute desired
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName: NSStringFromClass([Destinations class])];
request.predicate = [NSPredicate predicateWithFormat: @"dId == %@", sender.tag];
request.sortDescriptors = nil;
// Perform the search
NSError *error = nil;
NSArray *matches = [context executeFetchRequest: request error: &error];
// Use the search results
if( !matches || matches.count < 1 ) {
NSLog( @"Uh oh, got a nil back from my Destination fetch request!" );
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"No Info"
message: @"The database did not have information for this selection"
delegate: nil
cancelButtonTitle: @"OK"
otherButtonTitles: nil];
[alert show];
} else {
MyOtherViewController *movc = [[MyOtherViewContoller alloc] init];
movc.destDetails = [matches lastObject];
[self.navigationController pushViewController: movc animated: YES];
}
}
@end
我希望屏幕上永远不会有微调器,用户也不会有任何延迟,但是,由于我不知道建立数据库连接需要多长时间,我必须为此做好准备当用户按下按钮时没有准备好。
我无法在openOrCreateDbWithCompletionHandler:
完成时使用回调,因为我不想做任何事情,只有当用户按下按钮时才会这样做。
我考虑使用信号量,但似乎我只发信号一次(在openOrCreateDbWithCompletionHandler:
调用的完成处理程序中),但每按一次按钮就会等待它。这似乎只适用于第一个按钮推送。
我考虑在dispatch_group_async()
中使用openOrCreateDbWithCompletionHandler:
,然后dispatch_group_wait()
使用goButtonTouched:
,但由于openOrCreateDbWithCompletionHandler:
在另一个帖子上工作并立即返回,我不知道我认为等待状态会被设定。
我可以设置一个我自己的旗帜,比如openOrCreateDbWithCompletionHandler:
之前,self.notOpenYet = YES;
,然后在其完成处理程序中self.notOpenYet = NO;
,然后在goButtonTouched:
替换?DatabaseNotAvailableYet ??? with self.notOpenYet
,但是如何阻止其状态的进展?因为我不知道等待是等于纳秒还是几秒钟,因此放入循环和计时器似乎很麻烦。
这似乎是一个常见的情况,我相信你们都经常做这种事情,而且我的教育很差,但是我搜索了stackOverflow和网络,并没有找到令人满意的答案。 / p>
答案 0 :(得分:1)
我认为,阻止执行是一个坏习惯,除非你正在构建自己的事件循环,这很少是必要的。在你的情况下你也不需要做任何GCD的东西。只是感觉异步。
以下内容应该有效:
@implementation aViewController
- (void) viewDidLoad {
self.waitingForDB = NO;
self.databaseReady = NO;
[self.dbManager openOrCreateDbWithCompletionHandler: ^(NSError *err) {
if( err ){
NSLog( @"There was a problem opening the database" )
}else{
[self performSelectorOnMainThread:@selector(handleDatabaseReady) withObject:nil waitUntilDone:NO];
};
}];
}
- (void)handleDatabaseReady{
self.databaseReady = YES;
if(self.waitingForDB){
[self takeSpinnerOffScreen];
[self go];
}
}
- (IBAction) goButtonTouched: (id) sender {
// Wait here until the database is open and ready to use.
if( !self.databaseReady ) {
self.waitingForDB = YES;
[self putSpinnerOnScreen];
else{
[self go];
}
}
-(void)go{
// Use the database...
NSManagedObjectContext *context = [self theDatabaseContext];
// Build the search request for the attribute desired
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName: NSStringFromClass([Destinations class])];
request.predicate = [NSPredicate predicateWithFormat: @"dId == %@", sender.tag];
request.sortDescriptors = nil;
// Perform the search
NSError *error = nil;
NSArray *matches = [context executeFetchRequest: request error: &error];
// Use the search results
if( !matches || matches.count < 1 ) {
NSLog( @"Uh oh, got a nil back from my Destination fetch request!" );
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"No Info"
message: @"The database did not have information for this selection"
delegate: nil
cancelButtonTitle: @"OK"
otherButtonTitles: nil];
[alert show];
} else {
MyOtherViewController *movc = [[MyOtherViewContoller alloc] init];
movc.destDetails = [matches lastObject];
[self.navigationController pushViewController: movc animated: YES];
}
}
@end
在主线程上执行对handleDatabaseReady
的调用可确保不会出现设置/读取新属性的竞争条件。
答案 1 :(得分:0)
我会带着旗帜走。您不想阻止UI,只需显示微调器并从goButtonTouched返回。但是,您需要在openOrCreateDbWithCompletionHandler中取消微调器(如果它处于活动状态):。
答案 2 :(得分:-1)
这是一个相当简单的场景。你做了一个方法来做这些事情。让我们称之为doStuff
。在主线程中,您可以致电performSelectorInBackgroundThread:@selector(doStuff)
。默认情况下不要启用该按钮。在doStuff
结束时启用它,以便用户在准备好之前不会点按它。为了使其更具吸引力,您可以将微调器放在按钮的位置,然后在doStuff
完成时将其替换为按钮。
答案 3 :(得分:-1)
您可以使用许多类和API来实现此类操作。您可以将NSThread
与信号量和事件等同步原语一起使用,等待用户实际按下按钮时完成。您可以使用NSOperation
子类(带NSOperationQueue
),并且可以使用GCD队列。
我建议你看一下Apple的Concurrency Programming Guide中的一些信息。
在您的情况下,最好使用dispatch_async
将操作添加到GCD后台队列,并结合信号量,当用户点击按钮时,您可以等待信号量。您可以查看问题"How do I wait for an asynchronously dispatched block to finish?"以获取示例。