[update: this problem has been resolved; the issue was not in drawInRect:
but in UIGraphicsBeginImageContext()
]
In my app, I'm grabbing a bunch of large images, cropping them down to thumbnail size and storing the thumbnails for previewing.
Note that I'm doing this in a separate image context -- this is not about redrawing a UIView
that is on the screen.
This code is rather intensive so I'm running it in a separate thread. The actual scaling looks like this, and is a category implementation on top of 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;
}
This is called from a separate method, which loops through the images in turn and does the processing. The actual call to the above method looks like this:
UIImage *small = [bigger scaledImageWithWidth:24.f andHeight:32.f];
This all works most of the time, but occasionally I get an EXC_BAD_ACCESS
.
Backtrace:
#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 ()
[update 4] When I run this in the Simulator, and this problem happens, the console additionally shows the following:
// 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
My gut feeling is that I occasionally end up trying to drawInRect:
while the OS is also trying to draw something, which results, occasionally, in a crash. I always presumed that as long as you don't draw on the actual screen, this is acceptable -- is this not the case? Or if it is the case, any idea what might be causing this?
Update (r2): I forgot to mention that this app is running under rather severe memory constraints (I've got a lot of images loaded at any given time and these are swapped in/out), so this may be a case of running out of memory (read on -- it's not). I'm not sure how to verify that, though, so thoughts on this would be welcome too. I did verify this by severely cutting down on the number of images being loaded and adding a check to make sure they're properly deallocated (they are, and the crash still occurs).
Update 3: I thought I found the problem. Below is the answer I wrote, before the crash happened again:
The code would (after I posted this question) occasionally start exiting with exit code 0
, and sometimes with exit code 10 (SIGBUS)
. 0
means "no error", so that was extremely odd. 10
seems to mean a bit of everything so that was unhelpful too. The drawInRect:
call was a big hint, though, when the crash happened there.
The looping through to get the thumbnails was generating a lot of autoreleased images. I had an autorelease pool but it was wrapping the entire for loop. I added a second autorelease pool within the for
loop:
- (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];
}
I thought the above fixed the issue, until I ran the app and it crashed on me with "exit code 0" again just earlier. Back to the drawing board...