5

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

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

所有这些代码都作为操作添加到 NSOperationQueue 中,因此它将异步加载,并且如果图像的网络服务器很慢,则不会导致我的应用程序锁定。我添加了 NSLog 行,这样我就可以在控制台中看到这段代码何时完成执行。

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

延迟并不完全是由缓慢的网络服务器引起的......我可以在 Safari 中加载图像 URL,加载时间不到一秒钟,或者我可以使用相同的代码加载它而不使用 NSOperationQueue,它加载速度更快.

有什么方法可以减少我的图像显示之前的延迟但继续使用 NSOperationQueue?

4

2 回答 2

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就像名字所说的那样——对象被发送到将调度请求的选择器,只要运行循环可以到达它,就会在主线程上将对象作为单个参数给出。在这种情况下,“数据”是 NSOperation 隐式创建的线程中池上的自动释放对象。因为您需要它在使用之前保持有效,所以我使用了waitUntilDone:YES. 另一种方法是使数据成为您明确拥有的东西,并让主线程方法释放它。

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

于 2010-11-30T18:24:53.410 回答
1

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

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

[imageView performSelectorOnMainThread:@selector(setImage:)
                          withObject:img
                       waitUntilDone:NO];
于 2010-11-30T23:02:02.647 回答