Iphone 必须在主线程上执行单独上下文的drawInRect:for?
[更新:此问题已解决;问题不在Iphone 必须在主线程上执行单独上下文的drawInRect:for?,iphone,objective-c,multithreading,uiimage,Iphone,Objective C,Multithreading,Uiimage,[更新:此问题已解决;问题不在drawInRect:中,而是在UIGraphicsBeginImageContext()] 在我的应用程序中,我抓取了一堆大图像,将它们裁剪成缩略图大小,并存储缩略图以供预览 请注意,我是在一个单独的图像上下文中执行此操作的——这是关于重新绘制屏幕上的UIView的而不是 这段代码相当密集,所以我在一个单独的线程中运行它。实际的缩放如下所示,是在UIImage之上的一个类别实现: - (UIImage *) scaledImageWithWidth:(CGFloa
drawInRect:
中,而是在UIGraphicsBeginImageContext()
]
在我的应用程序中,我抓取了一堆大图像,将它们裁剪成缩略图大小,并存储缩略图以供预览
请注意,我是在一个单独的图像上下文中执行此操作的——这是关于重新绘制屏幕上的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;
}
这在大多数情况下都有效,但偶尔我会得到一个EXC\u BAD\u访问
回溯:
#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
我认为上面的方法解决了这个问题,直到我运行了这个应用程序,它在我身上再次崩溃,并在早些时候出现了“退出代码0”。回到绘图板…我觉得您不应该直接调用
drawInRect
,因为它可能不是线程安全的。您应该在视图上调用setNeedsDisplay
,该视图将在安全的情况下向drawInRect
发送消息。您看过Matt Gemmell的最新版本吗?我从他在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();
不确定线程安全是否是问题所在,但在走得太远之前,不妨尝试一下Matt的代码。事实证明,有两个答案: “必须在主线程上执行单独上下文的
drawInRect:
”答案是否定的,它没有
但是,UIGraphicsBeginImageContext
必须。事实上,这就是发生碰撞的原因。直到(无效)图形上下文被更改后,崩溃才出现,这就是为什么崩溃发生在drawInRect:
行上
解决方案是停止使用
UIGraphicsContext
,而是使用CGBitmapContext
,后者是线程安全的 如果你看一下代码,你会发现这是不可能的,因为我是在一个单独的图像上下文中绘制rect的。通过观看由苹果员工提供的斯坦福iTunes U教程,他们明确表示你从不直接调用drawRect。我不知道这是否也适用于drawInRect,但事实并非如此。大约有5个苹果公司的例子可以这样做。这里有一个例子(来自烹饪书示例代码):有趣。我会看看的。谢谢你的提示!我仔细观察了一下,甚至试着用同样的方法做(虽然我已经有点像了),但是运气不好。相同的调用(drawInRect:),相同的错误。从iOS4.0开始,在UIKit中绘制图形上下文现在是线程安全的。具体来说:用于访问和操作图形上下文的例程现在可以正确处理驻留在不同线程上的上下文。谢谢你的发现,MattyG。当然很有趣。@Kalle:那么你更改了什么代码?你能分享一下吗?@FahimParkar这个答案已经有4年了。我不记得了,所以你最好自己问一个新问题。
- (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];
}
// 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();