将NSTimer放在单独的线程中

时间:2010-04-26 18:22:46

标签: cocoa multithreading

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

我正在尝试在单独的线程中设置NSTimer,以便在用户与我的应用程序的UI交互时继续触发。这似乎有效,但Leaks报告了一些问题 - 我相信我已将其缩小到我的计时器代码。

目前正在发生的事情是updateTimer尝试访问绑定到我的应用程序界面中的NSTableView的NSArrayController(timersController)。从那里,我抓住第一个选定的行并更改其timeSpent列。注意:timersController的内容是通过Core Data生成的托管对象的集合。

从阅读开始,我相信我应该尝试做的是在主线程上执行updateTimer函数,而不是在我的计时器辅助线程中执行。

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

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];
}

更新

关于这次泄漏,我仍然完全迷失了。我知道我显然做错了什么,但我已经将我的应用程序剥离到了它的骨头,似乎仍然无法找到它。为简单起见,我已将我的应用程序控制器代码上传到:a small pastebin。请注意,我现在已经删除了计时器线程代码,而是选择在单独的runloop中运行计时器(如此处所示)。

如果我设置Leaks Call Tree来隐藏Missing Symbols和System Libraries,我会看到以下输出:

编辑:屏幕截图的链接已被删除。

1 个答案:

答案 0 :(得分:51)

如果您产生新线程的唯一原因是允许您的计时器在用户与UI交互时运行,您可以将其添加到不同的runloop模式中:

NSTimer *uiTimer = [NSTimer timerWithTimeInterval:(1.0 / 5.0) target:self selector:@selector(uiTimerFired:) userInfo:nil repeats:YES];      
[[NSRunLoop mainRunLoop] addTimer:uiTimer forMode:NSRunLoopCommonModes];

作为这个答案的补充,现在可以使用Grand Central Dispatch和块来安排计时器:

// 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);