In many of the iPhone samples, the recommended way of drawing onto off-screen bitmap context begins by getting a bitmap context using the UIGraphicsBeginImageContext() API call. This works great if your drawing code is in the main UI thread, but in some cases you may want to do some processing in a background thread as well. The prime candidate for this is, when you download an image in a background thread and do some resizing/cropping shadows, borders on that image. You would still want to do that in the background thread in order not to block the UI thread and present a smooth experience. That is exactly what I was doing in my latest project (which BTW you can see the work in progress here).
The code works most of the time if you use the UIGraphicsBeginImageContext, but then crashes randomly at some random CoreGraphics calls. The culprit in this case sounded like it was some thread safety issue, and turns out it indeed was. As documented in the SDK, when this API call is made, the drawing environment is pushed onto the graphics context stack immediately. And that seems to be what is causing our threading issues. The context stack becomes shared between the UI thread which does its regular drawing code and the background thread which does the background image manipulation.
Luckily fixing this problem is not hard. All you need to do is to stop using the UIKit APIs and go back to the lower level Quartz APIs.
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, BITS_PER_COMPONENT, NUM_OF_COMPONENTS * size.width, colorSpaceRef, kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(colorSpaceRef); CGRect rect = CGRectMake(0.0, 0.0, size.width, size.height); CGContextClearRect(context, rect); // Do your drawing and image manipulation.. CGImageRef cgImage = CGBitmapContextCreateImage(context); UIImage *image = [UIImage imageWithCGImage:cgImage]; CGImageRelease(cgImage); CGContextRelease(context);
at 5:52 pm