3

我有自己的图像下载器类,它包含一个队列并一次下载一个(或一定数量)图像,将它们写入缓存文件夹并在必要时从缓存文件夹中检索它们。我还有一个 UIImageView 子类,我可以将 URL 传递给它,通过图像下载器类,它会查看图像是否已经存在于设备上,如果存在则显示它,或者在完成后下载并显示它。

图像下载完成后,我执行以下操作。我从下载的 NSData 创建一个 UIImage,将下载的 NSData 保存到磁盘并返回 UIImage。

// This is executed in a background thread
downloadedImage = [UIImage imageWithData:downloadedData];
BOOL saved = [fileManager createFileAtPath:filePath contents:downloadedData attributes:attributes];
// Send downloadedImage to the main thread and do something with it

要检索现有图像,我会这样做。

// This is executed in a background thread
if ([fileManager fileExistsAtPath:filePath])
{
    NSData* imageData = [fileManager contentsAtPath:filePath];
    retrievedImage = [UIImage imageWithData:imageData];
    // Send retrievedImage to the main thread and do something with it
}

如您所见,我总是直接从下载的 NSData 创建 UIImage,我从不使用 UIImagePNGRepresentation 创建 NSData,因此图像永远不会被压缩。当您从压缩的 NSData 创建 UIImage 时,UIImage 将在主线程上渲染之前对其进行解压缩,从而阻塞 UI。由于我现在有一个 UITableView,其中包含大量必须从磁盘下载或检索的小图像,因此这是不可接受的,因为它会极大地减慢我的滚动速度。

现在我的问题。用户还可以从相机胶卷中选择一张照片,保存它,它也必须出现在我的 UITableView 中。但我似乎无法找到一种方法将 UIImage 从相机胶卷转换为 NSData 而不使用 UIImagePNGRepresentation。所以这是我的问题。

如何将 UIImage 转换为未压缩的 NSData,以便稍后使用 imageWithData 将其转换回 UIImage,以便在渲染前不必解压缩?

或者

有什么方法可以在将 UIImage 发送到主线程并将其缓存之前进行解压缩,因此只需解压缩一次?

提前致谢。

4

3 回答 3

3

如何将 UIImage 转换为未压缩的 NSData,以便稍后使用 imageWithData 将其转换回 UIImage,以便在渲染前不必解压缩?

我认为您在这里真正要问的是如何将 UIImage 存储在磁盘上,以便您以后可以尽快从磁盘读取 UIImage。您并不关心它是否存储为 NSData;您只是希望能够快速阅读它。我建议你使用 ImageIO 框架。通过图像目标保存并稍后通过图像源获取。

http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Conceptual/ImageIOGuide/ikpg_dest/ikpg_dest.html

有什么方法可以在将 UIImage 发送到主线程并将其缓存之前进行解压缩,因此只需解压缩一次?

是的,好问题。那将是我的第二个建议:使用线程。这就是人们一直对桌子所做的事情。当桌子要求提供图像时,您要么已经拥有图像,要么没有。如果不这样做,则提供填充图像,并在后台获取真实图像。当真实图像准备好时,您已安排收到通知。回到主线程,您告诉表视图再次请求该行的数据;这次你得到了图像并提供了它。因此,用户将在图像出现之前看到轻微的延迟。我敢肯定,您已经看到很多这样的应用程序(《纽约时报》就是一个很好的例子)。

我还有一个建议,它可能是最好的。你说从磁盘解压缩图像需要时间。但是,如果图像很小,这根本不需要时间。但是图像应该很小,因为它会进入一个小地方 - 一个表格单元格。换句话说,您应该在第一次收到图像时预先缩小图像,以便在被问到时准备好每个图像的版本。将要进入小空间的大图像提供给时间和内存是巨大的浪费。

稍后添加:当然,您确实明白,如果您不将图像保存到磁盘,那么很多这种担心都是不必要的。我完全不清楚你为什么需要这样做。我希望你有充分的理由;但显然,如果您只是将图像保存在内存中,那么它的速度要快得多。

于 2013-03-29T00:34:15.063 回答
1

我找到了解决方案:

CGImageRef downloadedImageRef = downloadedImage.CGImage;
CGDataProviderRef provider = CGImageGetDataProvider(downloadedImageRef);
NSData *data = CFBridgingRelease(CGDataProviderCopyData(provider));
// Then you can save the data
于 2015-04-24T10:43:41.753 回答
0

如果您下载数据并将其保存到磁盘,则数据将以 PNG、JPEG 或 GIF 格式压缩。您不会下载未压缩的图像数据。因此,在将文件保存到磁盘之前,需要先解决有关首先进行解压缩的问题的根源。在保存之前解压会使文件变大很多,但这意味着在将数据读回CGImageRef或UIImage之前不需要解压。加载然后解压缩一堆图像会减慢 CPU 的速度并使滚动变慢。但是,简单地将所有内容保存在已经解压缩的内存中并不是一个解决方案,因为这将耗尽您所有的应用程序内存并很快使您的手机崩溃。您也许可以通过少量图像摆脱它,但这是第一次编写代码时需要解决的基本设计缺陷。如果你喜欢,你可以看看我关于这个主题的博客文章video-and-memory-usage-on-ios-devices,该帖子处理视频,但是在处理许多不同的图像时您会遇到完全相同的问题。我建议您以 TIFF 或 BMP 等未压缩格式将小图像写入磁盘,这样只要 ImageIO 支持该特定格式,就可以轻松读取它们。

于 2013-06-18T23:57:58.527 回答