Должен ли drawInRect: для отдельного контекста выполняться в основном потоке?
[обновление: эта проблема была решена; проблема не была в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;
}
Это вызывается из отдельного метода, который по очереди просматривает изображения и выполняет обработку. Фактический вызов вышеуказанного метода выглядит следующим образом:
UIImage *small = [bigger scaledImageWithWidth:24.f andHeight:32.f];
Это все работает большую часть времени, но иногда я получаю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 ()
[обновление 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:
звонок был большим намеком, однако, когда там произошел сбой.
Цикл, чтобы получить миниатюры, генерировал много автоматически выпущенных изображений. У меня был пул авто-релиза, но он оборачивал весь цикл 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» снова только ранее. Вернуться к доске для рисования...