当NSThread返回释放的对象? (苹果手机)

时间:2023-01-21 09:42:01

I have got a memory bug that seems to boil down to something happening in a thread. I am having difficulties troubleshooting this.

我有一个内存错误,似乎归结为一个线程中发生的事情。我在排除故障时遇到了困难。

I have a UIViewController, that when active, i.e. the user is using its view, retrieves updates from a web service in an NSThread.

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

This is done every 3 minutes and this delay is controlled by a:

这是每3分钟完成一次,这个延迟由以下内容控制:

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

The timerDone method now starts the NSThread that retrieves the web service data and also it sends the performSelector message again. This is a little "check for updates, populate views, shut everything down, repeat" routine that works just fine.

timerDone方法现在启动NSThread,它检索Web服务数据,并再次发送performSelector消息。这有点“检查更新,填充视图,关闭所有内容,重复”例程,工作得很好。

Now, the user can of course suddenly tap a button an load up a second UIViewController. When this happens I call:

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

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

And do my cleaning up in the dealloc method.

并在dealloc方法中进行清理。

My question is now: What happens if the NSThread was running while the user changed the view and set in motion the deconstruction of this object that is the starting point of the NSThread?

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

Should I keep a BOOL around that tells me if the NSThread is still active, and if so, what to do with the NSThread if this is the case.

我应该保持一个BOOL,告诉我NSThread是否仍然有效,如果是,那么如果是这样的话,如何处理NSThread。

The threading is done like this:

线程是这样完成的:

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

1 个解决方案

#1


7  

You have two problems here: first, you're using performSelector:withObject:afterDelay: to do what an NSTimer does best (periodic callback). cancelPreviousPerformRequestsWithTarget:selector:object: can be quite expensive, and because of your threading is likely creating race conditions.

这里有两个问题:第一,你正在使用performSelector:withObject:afterDelay:来做NSTimer最擅长的事情(定期回调)。 cancelPreviousPerformRequestsWithTarget:selector:object:可能非常昂贵,而且由于你的线程可能会造成竞争条件。

Second problem: each thread has its own run loop, and both mechanisms (performSelector:... and NSTimer) and are tied to the current thread's run loop.

第二个问题:每个线程都有自己的运行循环,并且两个机制(performSelector:...和NSTimer)都绑定到当前线程的运行循环。

Here's what I recommend: Create a single, long-lived NSThread with its own explicit run loop for all your update needs. Look at the Threading Programming Guide for some good example code of this. On that thread, set up a 3-minute repeating NSTimer. Every 3 minutes, update.

以下是我的建议:使用自己的显式运行循环创建一个长寿命的NSThread,以满足您的所有更新需求。请参阅“线程编程指南”以获取一些很好的示例代码。在该线程上,设置一个3分钟的重复NSTimer。每3分钟更新一次。

If you need to schedule an update outside the three-minute cycle, then you use performSelector:onThread:withObject:waitUntilDone: to call your updateAllVisibileElements. The way I generally do this is to encapsulate all of the thread logic into a single object (WebServiceController or whatever). It creates it own NSThread and saves it in an ivar. Then I use code like this:

如果需要在三分钟周期之外安排更新,则使用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];
    }
}

One more note: you mention that the background thread "populates views." A background thread should never call into UIKit. UIKit is not thread safe and should only be called on the main thread. I typically achieve this by posting notifications onto the main thread which the view controllers observe. The "updating" object should not know anything about the UI. That breaks the Model-View-Controller paradigm of Cocoa.

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

#1


7  

You have two problems here: first, you're using performSelector:withObject:afterDelay: to do what an NSTimer does best (periodic callback). cancelPreviousPerformRequestsWithTarget:selector:object: can be quite expensive, and because of your threading is likely creating race conditions.

这里有两个问题:第一,你正在使用performSelector:withObject:afterDelay:来做NSTimer最擅长的事情(定期回调)。 cancelPreviousPerformRequestsWithTarget:selector:object:可能非常昂贵,而且由于你的线程可能会造成竞争条件。

Second problem: each thread has its own run loop, and both mechanisms (performSelector:... and NSTimer) and are tied to the current thread's run loop.

第二个问题:每个线程都有自己的运行循环,并且两个机制(performSelector:...和NSTimer)都绑定到当前线程的运行循环。

Here's what I recommend: Create a single, long-lived NSThread with its own explicit run loop for all your update needs. Look at the Threading Programming Guide for some good example code of this. On that thread, set up a 3-minute repeating NSTimer. Every 3 minutes, update.

以下是我的建议:使用自己的显式运行循环创建一个长寿命的NSThread,以满足您的所有更新需求。请参阅“线程编程指南”以获取一些很好的示例代码。在该线程上,设置一个3分钟的重复NSTimer。每3分钟更新一次。

If you need to schedule an update outside the three-minute cycle, then you use performSelector:onThread:withObject:waitUntilDone: to call your updateAllVisibileElements. The way I generally do this is to encapsulate all of the thread logic into a single object (WebServiceController or whatever). It creates it own NSThread and saves it in an ivar. Then I use code like this:

如果需要在三分钟周期之外安排更新,则使用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];
    }
}

One more note: you mention that the background thread "populates views." A background thread should never call into UIKit. UIKit is not thread safe and should only be called on the main thread. I typically achieve this by posting notifications onto the main thread which the view controllers observe. The "updating" object should not know anything about the UI. That breaks the Model-View-Controller paradigm of Cocoa.

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