多线程ViewController中的UIWebView

时间:2009-06-03 14:27:18

标签: iphone objective-c cocoa-touch memory-management uikit

我在viewcontroller中有一个UIWebView,它有两个方法如下。问题是如果我在第二个线程完成之前弹出(点击导航栏)此控制器,应用程序将在[super dealloc]之后崩溃,因为“试图从主线程以外的线程获取Web锁定或这可能是从辅助线程调用UIKit的结果。“任何帮助都会非常感激。

-(void)viewDidAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(load) object:nil];
    [operationQueue addOperation:operation];
    [operation release];
}

-(void)load {
    [NSThread sleepForTimeInterval:5];
    [self performSelectorOnMainThread:@selector(done) withObject:nil waitUntilDone:NO];
}

8 个答案:

答案 0 :(得分:35)

我有一个相同的解决方案,其中后台线程是最后一个版本,导致视图控制器的dealloc发生在后台线程中,最终导致相同的崩溃。

上面的[[self retain] autorelease]仍然会导致最终版本从后台线程的自动释放池中发生。 (除非有关于自动释放池的发布有什么特别之处,我很惊讶这会产生影响)。

我发现这是我理想的解决方案,将此代码放入我的视图控制器类中:

- (oneway void)release
{
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
    } else {
        [super release];
    }
}

这确保了我的视图控制器类的release方法总是在主线程上执行。

我有点惊讶的是,某些只能从主线程中正确释放的对象还没有内置的东西。哦,好吧......

答案 1 :(得分:12)

这是在主线程上运行UIKit元素的一些代码。如果您正在处理不同的线程并且需要运行一段UIKit代码,请将其放在此Grand Central Dispatch代码段的括号中。

dispatch_async(dispatch_get_main_queue(), ^{

    // do work here

});

答案 2 :(得分:1)

通常,当使用它们的视图消失时,您应该取消任何后台操作。如:

- (void)viewWillDisappear:(BOOL)animated {
  [operationQueue cancelAllOperations];
  [super viewWillDisappear:animated;
}

答案 3 :(得分:1)

我目前在我的应用程序中遇到了类似的问题。显示UIWebView的视图控制器被推送到导航控制器并启动后台线程以检索数据。如果在线程完成之前点击后退按钮,应用程序将崩溃并显示相同的错误消息。

问题似乎是NSThread保留了目标(self)和object(参数)并在方法运行后释放它 - 不幸的是它从线程中释放。因此,当控制器被创建时,保留计数为1,当线程启动时,控制器获得保留计数2.当在线程完成之前弹出控制器时,导航控制器释放控制器,这导致保留计数1.到目前为止这很好 - 但是如果线程最终完成,NSThread释放控制器,这导致保留计数为0并且线程内立即释放。这使得UIWebView(在控制器的dealloc方法中发布)引发该线程警告异常并崩溃。

我通过使用[[self retain] autorelease]作为线程中的最后一个语句(在线程释放其池之前)成功解决了这个问题。这可确保控制器对象不会立即释放,但会在主线程的运行循环中标记为自动释放并取消分配。然而,这是一个有点肮脏的黑客,我宁愿找到更好的解决方案。

答案 4 :(得分:1)

我试过了:

[self retain];
[self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];

似乎工作得更好。

答案 5 :(得分:0)

我不确定你的代码到底发生了什么,但看起来viewDidAppear正在调用并创建第二个线程,然后你导航离开控制器并释放它,然后第二个线程结束并在已释放的对象“self”上调用performSelectorOnMainThread。我想您可能只需检查发布是否未发生?

您收到的错误消息暗示您正在从第二个线程运行一些UIKit代码。 Apple最近为UIKit的线程调用添加了一些检查,我想你可能只需要重构你的load函数来更新主线程上的UI,而不是从第二个线程调用UIWebView函数。

希望有所帮助!

答案 6 :(得分:0)

我尝试了上面发布的两个解决方案[operationQueue cancelAllOperations][[self retain] autorelease]。但是,通过快速单击,仍然存在保留计数降至0并且类在辅助线程上取消分配的情况。为了避免崩溃,我暂时在dealloc中添加以下内容:

    if ([NSThread isMainThread]) {
        [super dealloc];
    }

这是一个明显的泄漏,但似乎是2个邪恶中的较小者。

欢迎任何遇到此问题的人提供任何其他见解。

答案 7 :(得分:0)

- (void)dealloc
{
    if(![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(dealloc) 
                               withObject:nil 
                            waitUntilDone:[NSThread isMainThread]];
        return;
    } 
    [super dealloc];
}