Specifies whether image decoding and caching should happen at image creation time.
在Objc.io #7彼得·斯坦伯格(Peter Steinberger)建议这样做:
+ (UIImage *)decompressedImageWithData:(NSData *)data { CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data,NULL); CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source,(__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),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方法吗?
解决方法
>这种“新方法”需要在主线程上进行某种渲染.您可以加载图像并设置应该缓存立即标志,但这将在主线程中设置一些操作进行处理.当您加载滚动视图和集合视图时,这将导致口吃.对于我来说,它比在后台派遣队列更旧的方式.
>如果您使用自己的内存缓冲区而不是文件,则需要创建复制数据的数据提供者,因为数据提供者希望内存缓冲区能够挂起.这听起来很明显,但是这个功能中的标志让你相信你可以这样做:
>从一些来源的压缩JPEG数据填充你自己的缓冲区
>创建一个数据提供者并附加JPEG数据
使用CACHE立即设置数据提供者创建图像源
>使用图像源创建一个CG图像
把所有的中间对象拖出去,把你很好地解压缩的CGImage对象直接放到一个可以滚动的UIImage对象上
它不会这样做,因为它会等待主线程进行解压缩.它认为一切都OK,因为它引用了您发布的所有这些中间对象.您发布了所有这些对象,认为它会立即解压缩,就像旗帜所说的那样.如果你也抛出了这个内存缓冲区,并且内存缓冲区是以非复制的方式传递的,那么你将会遇到垃圾.或者,如果内存缓冲区在我的情况下被重用,加载另一个图像,你也会得到垃圾.
你实际上没有办法知道这个图像何时会被解压缩并准备使用.
TL; DR =“考虑到kCGImageSourceShouldCacheImmediately意味着当方便到操作系统”
当你这样做的“老路”时,你们100%知道会有什么可以和什么时候.因为它不是延迟,你可以避免一些复制.我不认为这个API正在做任何神奇的事情,我认为这只是拿着内存缓冲区,然后在“引擎盖”下做“老方法”.
所以基本上没有免费的午餐.看着堆栈跟踪,从这个事情崩溃的时候,当我想到它被全部注销后,我重新使用我的内存缓冲区,我看到它呼叫到CA :: Transaction,CA :: Layer,CA :: Render和进入ImageProviderCopy …一直到JPEGParseJPEGInfo(它崩溃访问我的缓冲区).
这意味着kCGImageSourceShouldCacheImmediately除了设置一个标志之外什么都不做,以便在创建它之后尽可能快地告诉图像在主线程中解压缩,而不是实际上完全按照你的想法意思(正在阅读).如果将图像提交到滚动视图进行显示,并且图像进行绘制,它将完成相同的操作.如果你幸运的话,滚动之间会有一些空闲的循环,这样会改善一些事情,但是基本上我觉得这样做比希望更多.