0

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

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

bool _WebTryThreadLock(bool), 0x4d99c60: 试图从主线程或web线程以外的线程获取web lock。这可能是从辅助线程调用 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];
    }
}

请帮忙!先感谢您。

4

1 回答 1

0

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

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

如果这是您的应用程序中的一个中心组件,我想这是因为您考虑释放“屏幕”的东西,我建议让您的控制器在 loadVIew 方法中显示“虚拟图像”,然后开始获取viewDidLoad 中的操作。您可以将 NSOperation 子类化,以便您只需将 URL 发送给它并让它做它的事情。

几周前我做了类似的事情,我不得不不断地启动线程操作,但很有可能用户会做一些导致这些操作被取消的事情。该功能是“构建”到 NSOperation 中的。 NSOperation 问题

于 2010-05-30T10:58:34.890 回答