iPhone 上的 NSAutoreleasePool 问题
Posted
技术标签:
【中文标题】iPhone 上的 NSAutoreleasePool 问题【英文标题】:NSAutoreleasePool issues on the iPhone 【发布时间】:2010-07-28 22:45:04 【问题描述】:我有一个小的 iPhone 应用程序,它在第一个视图上有一个按钮。当我选择这个按钮时,我会加载我的新视图,上面有一个图像。我目前正在使用以下代码在单独的线程上从在线源加载图像,同时允许用户继续控制应用程序:
- (void) loadImageTest
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [[NSURL alloc] init];
url = [NSURL URLWithString:logoPath];
NSData *data = [[NSData alloc] init];
data = [NSData dataWithContentsOfURL:url];
loadingImage = [UIImage imageWithData:data];
titleLogoImage.image = loadingImage;
//[pool drain];
[pool release];
这是从新视图的 init 中的这行代码调用的:
[NSThread detachNewThreadSelector:@selector(loadImageTest) toTarget:self withObject:nil];
现在这工作正常(ish),但如果我关闭新视图,然后快速连续(或在某些情况下只是之后)再次加载新视图,它将被经典的 EXC_BAD_ACCESS 炸毁。我确定这是由于线程池中的代码造成的,但是任何人都知道为什么会这样吗?
谢谢
【问题讨论】:
僵尸会救你的。 cocoadev.com/index.pl?NSZombieEnabled 【参考方案1】:你的:
// 没关系,你可以尝试使用 NSURLConnections 异步方法而不是 // 你自己的线程。 [NSThread detachNewThreadSelector:@selector(loadImageTest) toTarget:self withObject:nil];
- (void)loadImageTest
// This is fine
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// you're making and then abandoning this url object so it will leak
NSURL *url = [[NSURL alloc] init];
// this is fine
url = [NSURL URLWithString:logoPath];
// again making and abandoning an object
NSData *data = [[NSData alloc] init];
data = [NSData dataWithContentsOfURL:url];
// this works, but is not thread safe,
// can't operate on UIKit objects off the
// main thread
loadingImage = [UIImage imageWithData:data];
titleLogoImage.image = loadingImage;
// this is fine
//[pool drain];
[pool release];
清理以使事情线程安全等。应该有所帮助:
// I'm assuming you have a iVar for logoPath but we don't want to
// make threaded calls to an iVar (it's not mutable, so you could do it, but it's just bad form)
// If i'm wrong about logoPath being an iVar don't sweat copying it.
- (void)whateverMethodYouWant
NSString *aLogoPath = [[logoPath copy] autorelease];
[NSThread detachNewThreadSelector:@selector(loadImageForPath:) toTarget:self withObject:aLogoPath];
- (void)loadImageForPath:(NSString *)aLogoPath
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:aLogoPath]];
// the performSelector will retain the data until the method can be performed
[self performSelectorOnMainThread:@selector(setImageForTitleLogo:) withObject:imgData waitUntilDone:NO];
[pool release];
- (void)setImageForTitleLogo:(NSData *)imgData
// This part is not strictly necessary
// but it's a nice way to wrap a method that needs to happen on the main thread.
if ([NSThread isMainThread])
// make the image (i'm assuming you meant loadingImage to be a local scope variable)
UIImage *loadingImage = [UIImage imageWithData:imgData];
// make sure the button still exists
// also make sure you're setting any references to this button to nil when you're releasing and making new views
// so that you're not addressing a button that's been released
// (assigning the image to the button will cause the button to retain it for you)
if (titleLogoImage != nil)
titleLogoImage.image = loadingImage;
else
[self performSelectorOnMainThread:@selector(setImageForTitleLogo:) withObject:imgData waitUntilDone:NO];
【讨论】:
无论如何都很好 :) 我认为它没有在主线程上执行图像设置,这是问题所在。非常感谢:) 我时不时出现。很高兴它有帮助。【参考方案2】:你在做奇怪的事情。
NSURL *url = [[NSURL alloc] init];
表示您创建了一个您拥有的NSURL
对象。
url = [NSURL URLWithString:logoPath];
这意味着你创建了另一个 NSURL 对象,它是自动释放的。您刚刚创建的 NSURL 只是泄漏。这里的 NSData 也是如此。
loadingImage
必须由titleLogoImage
保留,否则它将在您的NSAutoReleasePool
耗尽时被释放。什么是titleLogoImage
,它是否保留image
?
edit ps:同样困扰我的是loadingImage
不局限于函数的范围。简而言之:
NSURL *url = [NSURL URLWithString:logoPath];
NSData *data = [NSData dataWithContentsOfURL:url];
titleLogoImage.image = [UIImage imageWithData:data];
至少可以省去一些麻烦。
【讨论】:
这就是我所拥有的,但是在阅读有关 NSAutoReleasePool 的信息时,我在某处读到了这样做......无论如何,无论如何,即使使用你之前的方法,我也会遇到同样的问题。还是谢谢。【参考方案3】:发布的代码中没有任何内容会触发崩溃。根据 titleLogoImage 的定义方式,它可能会受到自动释放的影响。
不过,beyond the problems outlined by mvds,您并没有迫切需要本地化自动释放池。它不会在这里做任何事情,只会给你带来麻烦。
自动释放池很危险,不适合初学者。他们将杀死其中的任何自动释放的对象。当您快速创建大量内存密集型对象时,您通常只创建自己的池。这似乎不是这里的情况。
尽管给予了关注,但很少使用自定义池。经过十多年的 Apple API 工作,我使用自己的自定义池的次数可能数不胜数。
【讨论】:
以上是关于iPhone 上的 NSAutoreleasePool 问题的主要内容,如果未能解决你的问题,请参考以下文章
在横向屏幕上旋转不是 iPhone 5 和 iPhone 6 上的视图