将NSTimer放在单独的线程中

ndg*_*ndg 8 cocoa multithreading

注意:可能需要向下滚动才能阅读我的编辑内容.

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

目前正在发生的事情是updateTimer尝试访问NSArrayController(timersController),它绑定到我的应用程序界面中的NSTableView.从那里,我抓住第一个选定的行并更改其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];
}
Run Code Online (Sandbox Code Playgroud)

UPDATE

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

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

编辑:链接到屏幕截图,因此删除.

sbo*_*oth 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];
Run Code Online (Sandbox Code Playgroud)

作为这个答案的补充,现在可以使用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);
Run Code Online (Sandbox Code Playgroud)

以后不再需要计时器时:

dispatch_source_cancel(_timer);
Run Code Online (Sandbox Code Playgroud)