1

我有一个内存错误,似乎可以归结为线程中发生的事情。我在解决这个问题时遇到了困难。

我有一个 UIViewController,当它处于活动状态时,即用户正在使用它的视图时,它会从 NSThread 中的 Web 服务中检索更新。

这是每 3 分钟完成一次,此延迟由以下项控制:

[self performSelector:@selector(timerDone) withObject:nil afterDelay:180.0];

timerDone方法现在启动检索 Web 服务数据的 NSThread 并performSelector再次发送消息。这是一个“检查更新、填充视图、关闭一切、重复”的小程序,效果很好。

现在,用户当然可以突然点击一个按钮来加载第二个 UIViewController。发生这种情况时,我会打电话:

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(timerDone) object:nil];

并在方法中进行清理dealloc

我现在的问题是:如果 NSThread 正在运行,而用户更改视图并开始解构这个作为 NSThread 起点的对象,会发生什么?

我是否应该保留一个 BOOL 来告诉我 NSThread 是否仍然处于活动状态,如果是这样,如果是这种情况,如何处理 NSThread。

线程是这样完成的:

- (void) runTimer {

    [self performSelector:@selector(timerDone) withObject:nil afterDelay:180];
}

- (void) timerDone {

    [self performSelector:@selector(runTimer) withObject:nil afterDelay:2];
    [NSThread detachNewThreadSelector:@selector(updateAllVisibleElements) toTarget:self withObject:nil]; 

    }

- (void) updateAllVisibleElements  {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    //call approiate web service
    [pool release];
}
4

1 回答 1

7

您在这里有两个问题:首先,您正在使用performSelector:withObject:afterDelay:NSTimer最擅长的事情(定期回调)。cancelPreviousPerformRequestsWithTarget:selector:object:可能非常昂贵,并且由于您的线程可能会产生竞争条件。

第二个问题:每个线程都有自己的运行循环,并且两种机制(performSelector:...NSTimer)都与当前线程的运行循环相关联。

以下是我的建议:创建一个单一的、长期存在的 NSThread,它具有自己的显式运行循环,可满足您的所有更新需求。查看Threading Programming Guide以获得一些很好的示例代码。在那个线程上,设置一个 3 分钟的重复NSTimer。每 3 分钟更新一次。

如果您需要在三分钟周期之外安排更新,那么您performSelector:onThread:withObject:waitUntilDone:可以调用您的updateAllVisibileElements. 我通常这样做的方式是将所有线程逻辑封装到一个对象(WebServiceController 或其他)中。它创建自己的 NSThread 并将其保存在 ivar 中。然后我使用这样的代码:

- (void)requestUpdate
{
    if ([NSThread currentThread] != self.thread)
    {
        [self performSelector:@selector(update) onThread:self.thread withObject:nil waitUntilDone:NO];
        return;
    }
    else
    {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        //call approiate web service
        [pool drain];
    }
}

还有一点需要注意:您提到后台线程“填充视图”。后台线程永远不应该调用 UIKit。UIKit 不是线程安全的,只能在主线程上调用。我通常通过将通知发布到视图控制器观察的主线程来实现这一点。“更新”对象不应该对 UI 有任何了解。这打破了 Cocoa 的模型-视图-控制器范式。

于 2010-02-12T15:13:49.717 回答