如何重复循环dispatch_after语句?

我想有一个与他们的dispatch_after语句for循环。 问题是dispatch_after调用似乎不符合for循环。 换句话说,我希望它在dispatch_after块中的语句执行之后才开始for循环的下一次迭代。

我将如何做到这一点?

用例

我想在屏幕上显示文字。 传统上,我每秒显示一个字。 但是,根据单词的长度,我现在想要显示更长的单词稍长一点,短一点的单词稍微less一点时间。 我想提出一个词,稍等一会儿(取决于单词的长度),然后提出下一个单词,稍等一会儿,然后等下一个单词。

这是达到此目的的一种方法。 当然,你将需要用代码replace我的NSLog来显示这个单词,并用你正在使用的任何函数来replace我的简单的0.05 * word.length函数来确定延迟,但是这应该做到这一点,并没有阻止呈现线程。

 - (void)presentWord: (NSString*)word { // Create a private, serial queue. This will preserve the ordering of the words static dispatch_queue_t wordQueue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ wordQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); }); dispatch_async(wordQueue, ^{ // Suspend the queue dispatch_suspend(wordQueue); // Show the word... NSLog(@"Now showing word: %@", word); // Calculate the delay until the next word should be shown... const NSTimeInterval timeToShow = 0.05 * word.length; // Or whatever your delay function is... // Have dispatch_after call us after that amount of time to resume the wordQueue. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeToShow * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ dispatch_resume(wordQueue); }); }); } // There's nothing special here. Just split up a longer string into words, and pass them // to presentWord: one at a time. - (void)presentSentence: (NSString*)string { NSArray* components = [string componentsSeparatedByCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; [components enumerateObjectsUsingBlock:^(NSString* obj, NSUInteger idx, BOOL *stop) { [self presentWord: obj]; }]; } 

编辑:这个工作的方式是,我使用串行队列来维护单词的顺序。 当你向-presentWords提交一个单词时,它在-presentWords的“后面”排入一个块。 当这个块开始执行时,你知道wordQueue没有被挂起(因为你在一个在wordQueue上执行的块),而且我们所做的第一件事情就是挂起wordQueue 。 由于这个块已经“在飞行中”,它将会运行到完成,但是没有其他的块会从wordQueue运行,直到有人恢复。 暂停队列后,我们显示这个单词。 它将保持显示,直到显示其他内容。 然后,我们根据刚刚开始显示的单词的长度来计算延迟,并在此时间过去之后设置dispatch_after以恢复wordQueue 。 当串行队列恢复时,下一个字的块开始执行,挂起队列,整个过程重复进行。

打印0,1,2,3,4,5,6,7,8,9,每秒一位数。

  dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0UL); for (int i=0; i<10; i++) { dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_async(queue,^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC), dispatch_get_current_queue(), ^{ NSLog(@"%d",i); dispatch_sync(dispatch_get_main_queue(), ^{ // show label on screen }); dispatch_semaphore_signal(semaphore); }); }); } 

如果你陈述你的用例,也许还有其他方法可以完成你正在做的事情。


您也可以预先累计延迟时间并发送所有的块。

  (1) __block double delay = 0; (2) dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0UL); (3) for (int i=0; i<10; i++) { (4) delay += 1LL * NSEC_PER_SEC; // replace 1 second with something related to the length of your word (5) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay), queue, ^{ NSLog(@"%d",i); (6) dispatch_sync(dispatch_get_main_queue(), ^{ // show label on screen }); }); } 
  1. 标记variables__block,使其可以在块内部修改
  2. 获取系统的预定义队列之一。 一个队列保存了一些匿名的代码片段(称为“块”),这些代码依次执行。
  3. 循环。
  4. 加一秒钟 你应该用别的东西replace那个1。 例如,如果单词有10个字母,则.10 * [单词长度]将导致暂停1秒。
  5. 在给定数量的“延迟”秒后,将下面的代码(又名“块”)放在“队列”中。 队列将自动运行你放在它上面的代码块。 这条线就像是说“延迟后运行”。
  6. 任何时候你在后台(就像这里,因为“队列”可能在后台线程上运行)并且你想改变用户界面,你必须在主队列中运行代码,因为UIKit不是线程安全的库(意思是,如果你从不同的线程调用它,不是为了正确运行)。

更新2

这是一个基于GCD的版本:

 @interface Test : NSObject @property ( nonatomic, copy ) NSArray * wordlist ; @property ( nonatomic ) volatile BOOL cancelled ; -(void)run ; -(void)cancel ; @end @implementation Test -(void)displayWords:(NSArray*)words { NSLog(@"word=%@\n", words[0] ) ; if ( words.count > 1 && !self.cancelled ) { words = [ words subarrayWithRange:(NSRange){ 1, words.count - 1 } ] ; double delayInSeconds = 1.0; // calculate delay until next word here dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [ self performSelectorOnMainThread:@selector( displayWords: ) withObject:words waitUntilDone:NO ] ; }); } } -(void)run { [ self performSelectorOnMainThread:@selector( displayWords: ) withObject:self.wordlist waitUntilDone:NO ] ; } -(void)cancel { self.cancelled = YES ; } @end 

这将在主线程上执行显示 – 如果使用UIKit绘制文本,则这是一个要求。

这与其他答案类似,但是只使用默认队列而不使用dispatch_resume()dispatch_suspend()


你可以使用NSThread 。 我认为这是非常可以理解的:

 @interface Thread : NSThread @property ( nonatomic, copy ) NSArray * wordlist ; @end @implementation Thread // runs on main thread -(void)displayWord:(NSString*)word { // code to display word goes here } -(void)main { for( NSString * word in self.wordlist ) { if ( self.isCancelled ) { return ; } [ self performSelectorOnMainThread:@selector( displayWord: ) withObject:word waitUntilDone:NO ] ; [ NSThread sleepForTimeInterval:1.0 ] ; // replace with desired pause... (could be based on word length) } } @end 

使用:

 Thread * thread = [[ Thread alloc ] init] ; thread.wordlist = @[ @"one", @"two", @"three" ] ; [ thread start ] ;