0

我有一个小的 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 炸毁。我确定这是由于线程池中的代码造成的,但是任何人都可以看到为什么会这样吗?

谢谢

4

3 回答 3

2

你的:

// 没关系,你可以尝试使用 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];
    }
}
于 2010-07-29T03:41:29.627 回答
0

发布的代码中没有任何内容会触发崩溃。根据 titleLogoImage 的定义方式,它可能会受到自动释放的影响。

但是,除了 mvds 列出的问题之外,您没有迫切需要本地化自动释放池。它不会在这里做任何事情,只会给你带来麻烦。

自动释放池很危险,不适合初学者。他们将杀死其中的任何自动释放的对象。当您快速创建大量内存密集型对象时,您通常只创建自己的池。这似乎不是这里的情况。

尽管给予了他们关注,但很少使用自定义池。经过十多年的 Apple API 工作,我使用自己的自定义池的次数可能数不胜数。

于 2010-07-29T00:01:16.837 回答
0

你在做奇怪的事情。

NSURL *url = [[NSURL alloc] init];

意味着您创建一个NSURL您拥有的对象。

url = [NSURL URLWithString:logoPath];

这意味着你创建了另一个NSURL 对象,它是自动释放的。您刚刚创建的 NSURL 只是泄漏。这里的 NSData 也是如此。

loadingImage必须由 保留,titleLogoImage否则它将在您的NSAutoReleasePool. 什么是titleLogoImage,它保留了image什么?

编辑ps:同样让我感到不安的是,loadingImage它并不局限于函数的范围。简而言之:

NSURL *url = [NSURL URLWithString:logoPath];
NSData *data = [NSData dataWithContentsOfURL:url];
titleLogoImage.image = [UIImage imageWithData:data];

至少可以省去一些麻烦。

于 2010-07-28T23:30:04.117 回答