6
-(void)viewDidLoad{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [NSTimer scheduledTimerWithTimeInterval:0.10 
                                         target:self 
                                       selector:@selector(action_Timer) 
                                       userInfo:nil 
                                        repeats:YES];
        }
    );
}

-(void)action_Timer{
    LOG("Timer called");
}

action_Timer没有被调用。我不知道为什么。你有什么主意吗?

4

2 回答 2

18

+[NSTimer scheduledTimerWithTimeInterval:...]从 GCD 工作线程调用。GCD 工作线程不运行运行循环。这就是为什么你的第一次尝试没有奏效。

当您尝试时[[NSRunLoop mainRunLoop] addTimer:myTimer forMode:NSDefaultRunLoopMode],您正在从 GCD 工作线程向主运行循环发送消息。存在的问题NSRunLoop不是线程安全的。(这在NSRunLoop 类参考中有记录。)

相反,您需要分派回主队列,以便当您将addTimer:...消息发送到主运行循环时,它在主线程上完成。

-(void)viewDidLoad {
    [super viewDidLoad];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSTimer *timer = [NSTimer timerWithTimeInterval:0.10 
                                         target:self 
                                       selector:@selector(action_Timer) 
                                       userInfo:nil 
                                        repeats:YES];
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        });        
    });
}

实际上,如果您要在主运行循环中安排它,则没有理由在后台队列上创建计时器。您可以直接分派回主队列来创建和调度它:

-(void)viewDidLoad {
    [super viewDidLoad];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"on background queue");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"on main queue");
            [NSTimer scheduledTimerWithTimeInterval:0.10 
                                         target:self 
                                       selector:@selector(action_Timer) 
                                       userInfo:nil 
                                        repeats:YES];
        });        
    });
}

请注意,我的两个解决方案都将计时器添加到主运行循环中,因此计时器的操作将在主线程上运行。如果您希望计时器的操作在后台队列上运行,您应该从操作中分派给它:

-(void)action_Timer {
    // This is called on the main queue, so dispatch to a background queue.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        LOG("Timer called");
    });
}
于 2013-01-28T20:08:49.057 回答
1

您必须将计时器添加到主运行循环才能触发计时器,但首先,您应该在私有 ivar 或属性中保存对计时器的引用:

-(void)viewDidLoad{
        // on the main queue
        self.mytimer = [NSTimer scheduledTimerWithTimeInterval:0.10 
                                                        target:self 
                                                      selector:@selector(action_Timer) 
                                                      userInfo:nil 
                                                        repeats:YES];
        [[NSRunLoop mainRunLoop] addTimer:myTimer forMode:NSDefaultRunLoopMode];
}

-(void)action_Timer{
    dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
        LOG("Timer called");
    });
}

我发现在被调用的方法中更容易离开主队列。在某个时候,也许 inviewDidUnlod或 in dealloc,你会有 call [self.myTimer invalidate]; self.myTimer = nil;

于 2013-01-28T19:55:59.327 回答