我想要一个带有dispatch_after
语句的for循环。问题是dispatch_after调用似乎与for循环不一致。换句话说,我希望它只在dispatch_after
块中的语句执行后才开始for循环的下一次迭代。
我该怎么做?
我想在屏幕上显示文字。传统上我每秒显示一个单词。但是根据单词的长度,我现在想要显示更长的单词,稍长的单词,以及更短的单词。我想提出一个词,等一会儿(取决于这个词有多长),然后提出下一个词,等一会儿,然后是下一个,等等。
答案 0 :(得分:5)
打印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 :(得分:4)
这是实现这一目标的一种方法。当然,你需要用显示单词的代码替换我的NSLog,并用我用来确定延迟的任何函数替换我的简单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
提交一个单词时,它会在wordQueue
的“后面”排列一个块。当该块开始执行时,您知道wordQueue
未被挂起(因为您处于wordQueue
上正在执行的块中),我们要做的第一件事就是暂停wordQueue
。由于此块已经“在飞行中”,它将运行完成,但是在有人恢复之前,不会从wordQueue
运行其他块。暂停队列后,我们显示该单词。它将一直显示,直到显示其他内容。然后,我们根据刚刚开始显示的单词的长度来计算延迟,并在该时间过去之后设置dispatch_after
以恢复wordQueue
。当序列队列恢复时,下一个单词的块开始执行,暂停队列,整个过程重复进行。
答案 2 :(得分:0)
更新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 ] ;