图像解压缩的问题已在Stack Overflow中进行了大量讨论,但直到这个问题,才提到kCGImageSourceShouldCacheImmediately
,这是iOS 7中引入的一个选项,从理论上讲,它可以解决这个问题。从标题:
指定图像解码和缓存是否应在图像创建时发生。
在Objc.io #7中,Peter Steinberger提出了这种方法:
+ (UIImage *)decompressedImageWithData:(NSData *)data
{
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, (__bridge CFDictionaryRef)@{(id)kCGImageSourceShouldCacheImmediately: @YES});
UIImage *image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
CFRelease(source);
return image;
}
AFNetworking和SDWebImage等库仍然使用CGContextDrawImage
方法进行图像解压缩。来自SDWebImage:
+ (UIImage *)decodedImageWithImage:(UIImage *)image {
if (image.images) {
// Do not decode animated images
return image;
}
CGImageRef imageRef = image.CGImage;
CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
CGRect imageRect = (CGRect){.origin = CGPointZero, .size = imageSize};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask);
BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone ||
infoMask == kCGImageAlphaNoneSkipFirst ||
infoMask == kCGImageAlphaNoneSkipLast);
// CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB.
// https://developer.apple.com/library/mac/#qa/qa1037/_index.html
if (infoMask == kCGImageAlphaNone && CGColorSpaceGetNumberOfComponents(colorSpace) > 1) {
// Unset the old alpha info.
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
// Set noneSkipFirst.
bitmapInfo |= kCGImageAlphaNoneSkipFirst;
}
// Some PNGs tell us they have alpha but only 3 components. Odd.
else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3) {
// Unset the old alpha info.
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaPremultipliedFirst;
}
// It calculates the bytes-per-row based on the bitsPerComponent and width arguments.
CGContextRef context = CGBitmapContextCreate(NULL,
imageSize.width,
imageSize.height,
CGImageGetBitsPerComponent(imageRef),
0,
colorSpace,
bitmapInfo);
CGColorSpaceRelease(colorSpace);
// If failed, return undecompressed image
if (!context) return image;
CGContextDrawImage(context, imageRect, imageRef);
CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
UIImage *decompressedImage = [UIImage imageWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation];
CGImageRelease(decompressedImageRef);
return decompressedImage;
}
我的问题是我们应该转向iOS 7中的kCGImageSourceShouldCacheImmediately
方法吗?
答案 0 :(得分:4)
据我所知,实施方面存在一些问题。
这个"新方法"需要在主线程上进行某种渲染。您可以加载图像并设置应该缓存立即标志,但这将在主线程中设置一些操作来处理。加载滚动视图和集合视图时,这将导致卡顿。对于我而言,它比在后台使用调度队列执行旧方式更为口吃。
如果您使用自己的内存缓冲区而不是文件,则需要创建复制数据的数据提供程序,因为它看起来像数据提供程序期望内存缓冲区一样。这听起来很明显,但是这个功能中的标志让你相信你可以做到这一点:
虽然它没有这样做,因为它将等待进行解压缩的主线程。它认为一切都很好,因为它引用了你发布的所有这些中间对象。您释放了所有这些对象,认为它会立即解压缩,就像标志所说的那样。如果你丢弃了那个内存缓冲区,并且该内存缓冲区是以无副本的方式传递的,那么你最终会得到垃圾。或者,如果在我的情况下重用内存缓冲区,要加载另一个图像,你也会得到垃圾。
您实际上无法知道此图像何时将被解压缩并准备好使用。
TL; DR ="考虑kCGImageSourceShouldCacheImmediately意味着何时方便操作系统"
当你按照旧方式进行时,你100%知道什么是可用的以及什么时候可用。因为它没有推迟,你可以避免一些复制。我不认为这个API无论如何都在做任何神奇的事情,我认为它只是拿着内存缓冲区,然后按照旧的方式做事情"引擎盖下。
所以这里基本没有免费午餐。看看堆栈跟踪,当我在重新使用内存缓冲区之后,当我认为它已全部注销时,这个东西崩溃了,我看到它调用了CA :: Transaction,CA :: Layer,CA :: Render,以及进入ImageProviderCopy ......一直到JPEGParseJPEGInfo(崩溃访问我的缓冲区)。
这意味着kCGImageSourceShouldCacheImmediately nothing 除了设置一个标志,告诉图像在创建它后尽快在主线程中解压缩,而不是实际上立即,因为你认为IMMEDIATELY意味着(在读)。如果您将图像移交到滚动视图以显示并且图像进行绘制,那么它将完成相同的操作。如果你很幸运,在滚动之间会有一些空余的循环,这会改善一些事情,但基本上我认为它听起来更有希望它会比实际做的更多。
答案 1 :(得分:0)
可以这样:
+ (UIImage *)imageFromURL:(NSURL *)url {
UIImage *result = nil;
if ([url isFileURL]) {
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
if (source) {
NSDictionary * attributes = @{ (id)kCGImageSourceShouldCache : @YES };
CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, (__bridge CFDictionaryRef)attributes);
if (cgImage) {
result = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
}
CFRelease(source);
}
}
return result;
}
答案 2 :(得分:-2)
您可以尝试以下代码:
+(NSData *)imageData:(UIImage *)image
{
//1.0 == 100%
return UIImageJPEGRepresentation(image, 0.7);
}
干杯!