2010-07-08 105 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

相关问题