从URL异步地使用UIImage加载图像时出现显着延迟

时间:2010-11-30 06:04:10

标签: cocoa-touch image uiimage imageview nsoperationqueue

我正在尝试编写一个从网址加载图片的iPad应用。我使用以下图像加载代码:

    url = [NSURL URLWithString:theURLString];
    NSData *data = [NSData dataWithContentsOfURL:url];
    img = [[UIImage alloc] initWithData:data];
    [imageView setImage:img];
    [img release];
    NSLog(@"Image reloaded");

所有这些代码都被添加到NSOperationQueue作为操作,因此它会异步加载,如果图像的websever很慢,不会导致我的应用程序锁定。我添加了NSLog行,因此我可以在控制台中看到此代码执行完毕后。

我一直注意到代码完成后,我的应用程序中的图像会在约5秒后更新。但是如果我自己使用这个代码而不将它放在NSOperationQUeue中,它似乎几乎立即更新了图像。

滞后不是完全由缓慢的Web服务器引起的...我可以在Safari中加载图像URL并且加载时间不到一秒,或者我可以使用相同的代码加载它而不使用NSOperationQueue并加载它更快。

有没有办法在我的图像显示之前减少滞后但是继续使用NSOperationQueue?

2 个答案:

答案 0 :(得分:6)

根据文档,您编写的代码无效。 UIKit对象可能不会在主线程上的任何地方调用。我敢打赌,你所做的事情恰好在大多数方面都有效,但是没有成功地改变显示效果,屏幕由于其他原因而巧合地更新。

Apple强烈建议,如果您希望保持电池效率,则线程不是执行异步URL提取的方法。相反,您应该使用NSURLConnection并允许runloop组织异步行为。编写一个快速的方法,只是将数据累积到NSData并不难,因为它会在连接完成后将整个事情发布给一个委托,但假设你宁愿坚持你已经得到的东西我建议:

url = [NSURL URLWithString:theURLString];
NSData *data = [NSData dataWithContentsOfURL:url];
[self performSelectorOnMainThread:@selector(setImageViewImage:) withObject:data waitUntilDone:YES];

...

- (void)setImageViewImage:(NSData *)data
{
    img = [[UIImage alloc] initWithData:data];
    [imageView setImage:img];
    [img release];
    NSLog(@"Image reloaded");
}

performSelectorOnMainThread执行名称所说的内容 - 发送到的对象将在运行循环到达后,将主对象上作为单个参数给出的对象调度请求的选择器。在这种情况下,'data'是NSOperation隐式创建的线程中池上的自动释放对象。因为您需要它在使用之前保持有效,所以我使用了waitUntilDone:YES。另一种方法是使数据成为您明确拥有的东西并让主线程方法释放它。

此方法的主要缺点是,如果图像以压缩形式(例如JPEG或PNG)返回,则它将在主线程上解压缩。为了避免这种情况,如果没有对UIImage的行为进行经验猜测,超出所记录的安全性,你需要降低到C级并使用CoreGraphics。但我认为这样做超出了这个问题的范围。

答案 1 :(得分:1)

Tommy对于需要在主线程上执行所有UIKit工作是正确的。但是,如果您在后台操作队列上运行fetch,则无需使用NSURLConnection异步加载。此外,通过保持图像解码工作在后台操作,您将在解码图像时防止主线程阻塞。

您应该可以按原样使用原始代码,但只需将[imgView setImage:img]更改为:

[imageView performSelectorOnMainThread:@selector(setImage:)
                          withObject:img
                       waitUntilDone:NO];