我有一个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];
}
}
请帮忙!提前谢谢。
答案 0 :(得分:0)
是的,尝试保持线程化的东西同步真是令人生畏。 您描述的用例听起来非常适合NSOperation。 通过使用这种方法,您可以在控制器中将NSOperationQueue作为ivar,并在您的控制器dealloc方法中释放它。
当控制器视图在scrollView中可见时(viewWillAppear或loadView)开始使用添加到NSOperationQueue的NSOperation来检索图像,如果用户在操作完成之前滚动并且NSOperationQueue是发布后,它将负责向所有操作发送取消消息,并且通常会以有序的方式关闭所有内容。
如果这是你应用中的一个核心组件,我想这是因为你考虑发布“屏幕”的东西,我建议让你的控制器在loadVIew方法中显示“虚拟图像”然后在viewDidLoad中启动一个fetch操作。你可以继承NSOperation,这样你就可以发送它并让它做它的事情。
几周前我做了类似的事情,我不得不继续开始线程操作,但很有可能用户会做一些导致这些操作被取消的事情。该功能正在“构建”到NSOperation中。 NSOperation question