iPhone:在多线程环境中发布UIViewController的问题

时间:2010-05-30 08:44:40

标签: iphone memory-leaks nsautoreleasepool

我有一个UIViewController,在该控制器中,我从URL源获取图像。在单独的线程中获取图像,之后在主线程上更新用户界面。此控制器显示为UIScrollView父级中的页面,该父级实现为释放不再可见的控制器。

当线程在UIViewController发布之前完成获取内容时,一切正常 - 但是当用户在线程完成之前滚动到另一个页面时,控制器被释放,控制器的唯一句柄由发布releaseCount的线程拥有控制器等于1.现在,一旦线程耗尽NSAutoreleasePool,控制器就会释放,因为releaseCount变为0.此时,我的应用程序崩溃并且我收到以下错误消息:

bool _WebTryThreadLock(bool),0x4d99c60:尝试从主线程或Web线程以外的线程获取Web锁。这可能是从辅助线程调用UIKit的结果。现在崩溃......

回溯显示应用程序在对[super dealloc]的调用时崩溃并且它完全有意义,因为当池耗尽时,dealloc函数必须由线程触发。我的问题是,如何克服此错误并释放控制器而不泄漏内存?

我尝试的一个解决方案是在池耗尽之前调用[self retain],以便retainCount不会降为零,然后使用以下代码在主线程中释放控制器:

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

不幸的是,这没有成功。下面是在线程上执行的函数:

- (void)thread_fetchContent {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSURL *imgURL = [NSURL URLWithString:@"http://www.domain.com/image.png"];

    // UIImage *imgHotspot is declared as private - The image is retained 
    // here and released as soon as it is assigned to UIImageView

    imgHotspot = [[[UIImage alloc] initWithData:
         [NSData dataWithContentsOfURL: imgURL]] retain];

    if ([self retainCount] == 1) {

        [self retain]; // increment retain count ~ workaround
        [pool drain]; // drain pool

        // this doesn't work - i get the same error

        [self performSelectorOnMainThread:@selector(autorelease)
             withObject:nil waitUntilDone:NO];
    }

    else {

        // show fetched image on the main thread - this works fine!

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

        [pool drain];
    }
}

请帮忙!提前谢谢。

1 个答案:

答案 0 :(得分:0)

是的,尝试保持线程化的东西同步真是令人生畏。 您描述的用例听起来非常适合NSOperation。 通过使用这种方法,您可以在控制器中将NSOperationQueue作为ivar,并在您的控制器dealloc方法中释放它。

当控制器视图在scrollView中可见时(viewWillAppear或loadView)开始使用添加到NSOperationQueue的NSOperation来检索图像,如果用户在操作完成之前滚动并且NSOperationQueue是发布后,它将负责向所有操作发送取消消息,并且通常会以有序的方式关闭所有内容。

如果这是你应用中的一个核心组件,我想这是因为你考虑发布“屏幕”的东西,我建议让你的控制器在loadVIew方法中显示“虚拟图像”然后在viewDidLoad中启动一个fetch操作。你可以继承NSOperation,这样你就可以发送它并让它做它的事情。

几周前我做了类似的事情,我不得不继续开始线程操作,但很有可能用户会做一些导致这些操作被取消的事情。该功能正在“构建”到NSOperation中。 NSOperation question