2010-10-11 40 views
10

我有一个后台线程加载图像并在主线程中显示它们。我注意到,在后台线程几乎无关,因为实际的图像解码似乎在主线程中完成:在后台线程中解码图像?

alt text

到目前为止,我已经试过在后台调用[UIImage imageNamed:][UIImage imageWithData:]CGImageCreateWithJPEGDataProvider线程没有区别。有没有办法强制解码在后台线程上完成?

这里已经有a similar question,但它没有帮助。正如我写在那里,我尝试以下窍门:

@implementation UIImage (Loading) 

- (void) forceLoad 
{ 
    const CGImageRef cgImage = [self CGImage]; 

    const int width = CGImageGetWidth(cgImage); 
    const int height = CGImageGetHeight(cgImage); 

    const CGColorSpaceRef colorspace = CGImageGetColorSpace(cgImage); 
    const CGContextRef context = CGBitmapContextCreate(
     NULL, /* Where to store the data. NULL = don’t care */ 
     width, height, /* width & height */ 
     8, width * 4, /* bits per component, bytes per row */ 
     colorspace, kCGImageAlphaNoneSkipFirst); 

    NSParameterAssert(context); 
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); 
    CGContextRelease(context); 
} 

@end 

这一工程(强制图像进行解码),但它也触发显然昂贵的呼叫ImageIO_BGR_A_TO_RGB_A_8Bit

回答

7

正式来说,UIKit不是线程安全的(尽管在iOS 4中它已经变得更安全了,我相信),所以你不应该在后台线程上调用任何一种UIImage方法。在实践中,我认为如果你没有做任何导致UIKit对象想要重绘的事情,你就不会遇到问题,但这是你在运输代码时不应该依赖的经验法则 - 我提到它只是为了解释为什么你可能没有问题。如你发现的那样,CGImageRefs(包括在UIImage中包装时)继续以其编码形式引用源图像,除非和直到有人需要它们解码为止。您引用的'技巧'可以有效触发解码;一个更好的解决方案是直到你安全地回到主线程,完全避免UIImage,从CGImageCreateWithJPEGDataProvider开始,像上面那样创建一个单独的上下文,从JPEGDataProvider到新的上下文创建CGContextDrawImage,然后将新的上下文作为图像,释放JPEG数据提供者。

对于ImageIO_BGR_A_TO_RGB_A_8Bit,可能需要给CGColorSpaceCreateDeviceRGB()代替CGImageGetColorSpace(cgImage),以获得具有不依赖于源图像文件的颜色空间的上下文。

10

我遇到了类似的问题,在新的视网膜iPad上的高分辨率图像。大于屏幕尺寸(粗略)的图像会导致UI响应性出现严重问题。这些都是JPG格式,因此让它们在背景上解码似乎是正确的做法。我仍在努力收紧所有这些,但汤米的解决方案对我来说非常有用。我只是想提供一些代码,以帮助下一个人,当他们试图确定他们的UI为什么与大图像口吃。这就是我最终做的事情(这段代码在NSOperation中的后台队列中运行)。这个例子是我的代码和上面的代码的混合:

CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((CFDataRef)self.data); 
    CGImageRef newImage = CGImageCreateWithJPEGDataProvider(dataProvider, 
            NULL, NO, 
            kCGRenderingIntentDefault); 


    ////////// 
    // force DECODE 

    const int width = CGImageGetWidth(newImage); 
    const int height = CGImageGetHeight(newImage); 

    const CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); 
    const CGContextRef context = CGBitmapContextCreate(
                NULL, /* Where to store the data. NULL = don’t care */ 
                width, height, /* width & height */ 
                8, width * 4, /* bits per component, bytes per row */ 
                colorspace, kCGImageAlphaNoneSkipFirst); 

    NSParameterAssert(context); 
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), newImage); 
    CGImageRef drawnImage = CGBitmapContextCreateImage(context); 
    CGContextRelease(context); 
    CGColorSpaceRelease(colorspace); 

    ////////// 

    self.downloadedImage = [UIImage imageWithCGImage:drawnImage]; 

    CGDataProviderRelease(dataProvider); 
    CGImageRelease(newImage); 
    CGImageRelease(drawnImage); 

我仍然在优化这个。但目前看来表现相当不错。

+0

谢谢,效果很好。 – Morrowless 2014-06-11 07:09:39