2010-10-11 81 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