Cocoa 将NSTimer放置在单独的线程中

Cocoa 将NSTimer放置在单独的线程中,cocoa,multithreading,Cocoa,Multithreading,注意:可能值得向下滚动阅读我的编辑 我试图在一个单独的线程中设置一个NSTimer,这样当用户与我的应用程序的UI交互时,它就会继续启动。这似乎是可行的,但“泄密”报告了一些问题——我相信我已经把它缩小到我的计时器代码 目前的情况是,updateTimer试图访问绑定到我的应用程序界面中的NSTableView的NSArrayController(TimerController)。从那里,我抓取第一个选中的行并更改其TimePent列。注意:timersController的内容是通过核心数据生

注意:可能值得向下滚动阅读我的编辑

我试图在一个单独的线程中设置一个NSTimer,这样当用户与我的应用程序的UI交互时,它就会继续启动。这似乎是可行的,但“泄密”报告了一些问题——我相信我已经把它缩小到我的计时器代码

目前的情况是,updateTimer试图访问绑定到我的应用程序界面中的NSTableView的NSArrayController(TimerController)。从那里,我抓取第一个选中的行并更改其TimePent列。注意:timersController的内容是通过核心数据生成的托管对象的集合

通过阅读,我相信我应该尝试在主线程上执行updateTimer函数,而不是在计时器辅助线程中执行

我在这里发帖是希望有更多经验的人能告诉我这是否是我唯一做错的事情。阅读了苹果关于线程的文档后,我发现它是一个非常大的主题领域

NSThread *timerThread = [[[NSThread alloc] initWithTarget:self selector:@selector(startTimerThread) object:nil] autorelease];
[timerThread start];

-(void)startTimerThread
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    activeTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES] retain];

    [runLoop run];
    [pool release];
}
-(void)updateTimer:(NSTimer *)timer
{
    NSArray *selectedTimers = [timersController selectedObjects];
    id selectedTimer = [selectedTimers objectAtIndex:0];
    NSNumber *currentTimeSpent = [selectedTimer timeSpent];

    [selectedTimer setValue:[NSNumber numberWithInt:[currentTimeSpent intValue]+1] forKey:@"timeSpent"];
}
-(void)stopTimer
{
    [activeTimer invalidate];
    [activeTimer release];
}
更新

关于这次泄密,我仍然完全不知所措。我知道我显然做错了什么,但我已经将我的应用程序剥离到了最底层,似乎仍然找不到它。为简单起见,我已将应用程序控制器代码上载到:。请注意,我现在删除了计时器线程代码,而是选择在单独的runloop中运行计时器(如这里所建议的)

如果我将Leaks调用树设置为隐藏缺少的符号和系统库,则会显示以下输出:


编辑:屏幕截图链接已断开并因此被删除。

如果生成新线程的唯一原因是允许计时器在用户与UI交互时运行,则可以在不同的运行循环模式下添加它:

NSTimer *uiTimer = [NSTimer timerWithTimeInterval:(1.0 / 5.0) target:self selector:@selector(uiTimerFired:) userInfo:nil repeats:YES];      
[[NSRunLoop mainRunLoop] addTimer:uiTimer forMode:NSRunLoopCommonModes];
作为该答案的补充,现在可以使用大中央调度和块调度计时器:

// Update the UI 5 times per second on the main queue
// Keep a strong reference to _timer in ARC
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, (1.0 / 5.0) * NSEC_PER_SEC, 0.25 * NSEC_PER_SEC);

dispatch_source_set_event_handler(_timer, ^{
    // Perform a periodic action
});

// Start the timer
dispatch_resume(_timer);
稍后,当不再需要计时器时:

dispatch_source_cancel(_timer);

为什么要设置额外的运行循环?您的计时器可以正常地与主运行循环一起工作。pastebin链接已失效。这是一个更方便的解决方案,谢谢。我刚刚花了一点时间阅读runloops。不幸的是,它似乎对我的泄密没有帮助。我已经把它缩小到(我相信)updateTimer函数中的一个问题。我相信这与我如何更新托管对象NSArray中的timeSpent变量有关。如果您上面发布的代码是您实际的updateTimer函数,那么我觉得它还可以。不过,您确实需要小心,一旦计时器失效,就不要释放它。这确实是我实际的updateTimer函数。我已经更新了OP,以总结我所看到的问题(并给出所用代码的更新)。