【问题标题】:When NSThread returns to a released object? (iPhone)NSThread什么时候返回一个释放的对象? (苹果手机)
【发布时间】:2010-02-12 23:13:49
【问题描述】:

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

我有一个 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];
}

【问题讨论】:

    标签: iphone memory-management nsthread


    【解决方案1】:

    这里有两个问题:首先,您使用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 的模型-视图-控制器范式。

    【讨论】:

    • 并且,只是重复最后一段:UIKit 不是线程安全的,应该从主线程更新。有关从后台发回信息的方式,请参阅performSelectorOnMainThread:withObject:waitUntilDone:developer.apple.com/iphone/library/documentation/Cocoa/…:
    • 大家好,谢谢你们。我真的不希望我写的任何东西都被解释为“更新视图的 NSThread”后台线程通过委托设置模型上的数据。然后模型告诉所有控制器新数据可用。我需要正确理解这一点:我是否应该在我的 appDelegate 中设置一个 NSThread,它贯穿我的应用程序的整个运行。然后该线程将每 3 分钟运行一次 NSTimer 并更新我的数据?现在我对所有数据对象进行排序,超过 3 分钟的数据对象会被更新,但这只会在加载需要这些数据的视图时发生。
    • 我会使用贯穿应用程序的 NSThread。如果您只想在有人需要更新时更新,那么让视图控制器在模型上调用类似 setNeedsUpdate 的东西(就像视图的 setNeedsDisplay 一样)。或者,您可以在模型对象上创建 addObserver 和 removeObserver,并且视图对象会以这种方式设置自己。每 3 分钟,如果观察者>0,更新对象。如果避免不必要的更新确实有好处,我只会这样做。通常它连接到昂贵的服务器。下载 500 字节或 5000 字节通常差不多。
    • 感谢您的详细说明,Rob。它是一个按调用收费的网络服务,因此选择正确的方法非常重要:)我喜欢 addObserver/removeObserver 的想法。我可以运行一个线程,它运行 X 分钟。计时器。只有当观察者的数量 > 0 时,我才需要进行更新。这似乎使我的视图控制器与处理定时器和线程垃圾收集分离。作为奖励,这也为我提供了一种收集多个数据对象并批量更新它们的方法。再次感谢您!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多