2010-07-08 48 views
1

[更新:此問題已解決;這個問題不在drawInRect:,但在UIGraphicsBeginImageContext()]必須drawInRect:在主線程上執行一個單獨的上下文嗎?

在我的應用程序,我抓住一堆大圖像,剪裁縮略圖大小和存儲縮略圖預覽。

請注意,我在單獨的圖像上下文中執行此操作 - 這是而不是有關重新繪製屏幕上的UIView

此代碼相當密集,所以我在單獨的線程中運行它。實際的比例是這樣的,而且是對UIImage頂部的類別執行:

- (UIImage *) scaledImageWithWidth:(CGFloat)width andHeight:(CGFloat)height 
{ 
    CGRect rect = CGRectMake(0.0, 0.0, width, height); 
    UIGraphicsBeginImageContext(rect.size); 
    [self drawInRect:rect]; // <-- crashing on this line 
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return scaledImage; 
} 

這是從一個單獨的方法,它通過將圖像中的循環轉,做了處理調用。上述方法的實際調用看起來是這樣的:

UIImage *small = [bigger scaledImageWithWidth:24.f andHeight:32.f]; 

這一切工作的大部分時間,但偶爾我得到一個EXC_BAD_ACCESS

回溯:

#0 0x330d678c in ripc_RenderImage() 
#1 0x330dd5aa in ripc_DrawImage() 
#2 0x300e3276 in CGContextDelegateDrawImage() 
#3 0x300e321a in CGContextDrawImage() 
#4 0x315164c8 in -[UIImage drawInRect:blendMode:alpha:]() 
#5 0x31516098 in -[UIImage drawInRect:]() 
#6 0x0000d6e4 in -[UIImage(Scaling) scaledImageWithWidth:andHeight:] (self=0x169320, _cmd=0x30e6e, width=48, height=64) at /Users/me/Documents/svn/app/trunk/Classes/UIImage+Scaling.m:20 
#7 0x00027df0 in -[mgMinimap loadThumbnails] (self=0x13df00, _cmd=0x30d05) at /Users/me/Documents/svn/app/trunk/Classes/mgMinimap.m:167 
#8 0x32b15bd0 in -[NSThread main]() 
#9 0x32b81cfe in __NSThread__main__() 
#10 0x30c8f78c in _pthread_start() 
#11 0x30c85078 in thread_start() 

[更新4]當我在模擬器中運行這個,還有這個問題發生,控制檯還顯示以下內容:

// the below is before loading the first thumbnail 
<Error>: CGContextSaveGState: invalid context 
<Error>: CGContextSetBlendMode: invalid context 
<Error>: CGContextSetAlpha: invalid context 
<Error>: CGContextTranslateCTM: invalid context 
<Error>: CGContextScaleCTM: invalid context 
<Error>: CGContextDrawImage: invalid context 
<Error>: CGContextRestoreGState: invalid context 
<Error>: CGBitmapContextCreateImage: invalid context 
// here, the first thumbnail has finished loading and the second one 
// is about to be generated 
<Error>: CGContextSetStrokeColorWithColor: invalid context 
<Error>: CGContextSetFillColorWithColor: invalid context 

我的直覺是,我偶爾最終會嘗試drawInRect:,而操作系統也在嘗試繪製某些東西,偶爾會導致崩潰。我總是推測,只要你不畫實際的屏幕,這是可以接受的 - 是不是這種情況?或者如果是這樣的話,有什麼想法可能會造成這種情況?

更新(r2):我忘了提及這個應用程序運行在相當嚴重的內存限制下(我有很多圖像在任何給定的時間加載,這些交換進/出),所以這可能是內存不足的情況(繼續閱讀 - 不是)。但我不確定如何驗證,所以對此的想法也會受到歡迎。我通過嚴格減少正在加載的圖像數量並添加一個檢查以確保它們正確地釋放(它們是,並且崩潰仍然發生)來驗證這一點。

更新3:我以爲我發現了這個問題。下面是我寫的答案,在崩潰再次發生之前:

代碼會(在我發佈此問題後)偶爾以退出代碼0開始退出,有時退出代碼爲10 (SIGBUS)0意味着「沒有錯誤」,所以這是非常奇怪的。 10似乎意味着一切,所以這也無濟於事。但是,當碰撞發生在那裏時,電話drawInRect:是一個很大的暗示。

通過循環顯示縮略圖,生成了許多自動發佈的圖像。我有一個autorelease池,但它包裝整個for循環。我添加了第二個自動釋放池for循環中:

- (void)loadThumbnails 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    for (...) { 
     NSAutoreleasePool *cyclePool = 
      [[NSAutoreleasePool alloc] init]; // <-- here 
     UIImage *bigger = ...; 
     UIImage *small = [bigger scaledImageWithWidth:24.f andHeight:32.f]; 
     UIImage *bloated = [i scaledImageWithWidth:48.f andHeight:64.f]; 
     [cyclePool release]; // <-- ending here 
    } 
    [pool release]; 
} 

我想上面的固定問題,直到我跑的應用程序,它與「退出代碼0」又不僅限於之前墜毀,機上我。回到繪圖板...

回答

1

事實證明,有兩個答案:「必須drawInRect:單獨的上下文中的主線程上執行」

答案是否定的,不是。

但是,UIGraphicsBeginImageContext必須。這實際上是發生這種崩潰的原因。直到(無效的)圖形上下文被改變時,崩潰才顯示出來,這就是爲什麼在drawInRect:這一行上發生崩潰的原因。

解決方案是停止使用UIGraphicsContext而不是使用CGBitmapContext,其中線程安全。

+3

從iOS4.0開始繪製到UIKit中的圖形上下文現在是線程安全的。具體來說: 用於訪問和操作圖形上下文的例程現在可以正確處理駐留在不同線程上的上下文。參考:http://developer.apple.com/library/ios/#releasenotes/General/WhatsNewIniPhoneOS/Articles/iPhoneOS4.html#//apple_ref/doc/uid/TP40009559-SW29 – MattyG 2011-09-14 04:03:44

+0

感謝您的發現,MattyG。絕對有趣。 – Kalle 2011-10-03 16:10:56

+0

@Kalle:你改變了什麼代碼?你可以分享嗎? – 2014-10-02 09:52:58

0

我有一種感覺,你不應該直接調用drawInRect,因爲它可能不是線程安全的。你應該在視圖上打電話setNeedsDisplay,如果安全的話,它會發送信息到drawInRect

+0

如果你看看代碼,你會注意到,這是不可能的,因爲我在單獨的圖像上下文中繪製矩形。 – Kalle 2010-07-08 20:19:05

+0

從觀看由蘋果員工提供的斯坦福iTunes U教程,他們明確指出,您從不直接調用drawRect。但我不知道是否同樣適用於drawInRect。 – 2010-07-08 20:46:56

+0

它沒有。大約有5個Apple的例子可以完成這個調用。這裏有一個例子(從烹飪書的示例代碼):http://developer.apple.com/iphone/library/samplecode/iPhoneCoreDataRecipes/Listings/Classes_RecipeDetailViewController_m.html – Kalle 2010-07-08 20:51:06

2

你看過Matt Gemmell的最新版本,MGImageUtilities?我在github上提取這從他的來源:

// Create appropriately modified image. 
UIImage *image; 
UIGraphicsBeginImageContextWithOptions(destRect.size, NO, 0.0); // 0.0 for scale means "correct scale for device's main screen". 
CGImageRef sourceImg = CGImageCreateWithImageInRect([self CGImage], sourceRect); // cropping happens here. 
image = [UIImage imageWithCGImage:sourceImg scale:0.0 orientation:self.imageOrientation]; // create cropped UIImage. 
[image drawInRect:destRect]; // the actual scaling happens here, and orientation is taken care of automatically. 
CGImageRelease(sourceImg); 
image = UIGraphicsGetImageFromCurrentImageContext(); 
UIGraphicsEndImageContext(); 

如果不知道線程安全是問題,但它可能是值得之前走得太遠了這條道路努力馬特的代碼。

+0

有趣。將要看。謝謝你的提示! – Kalle 2010-07-08 20:38:43

+0

我仔細看了一眼,甚至試圖用同樣的方式做(雖然我已經是這樣了),但沒有運氣。相同的調用(drawInRect :),同樣的錯誤。 – Kalle 2010-07-09 08:49:18

相關問題