Сбой CATiledLayer при рисовании предварительно подготовленного CGPath
Я использую CATiledLayer в качестве вспомогательного слоя для моего UIView, который я поместил внутри UIScrollView. В методе init моего вида я создаю объект CGPathRef, который рисует простую линию. Когда я пытаюсь нарисовать этот путь внутри drawLayer:inContext, он иногда падает с EXEC_BAD_ACCESS (редко), когда я прокручиваю / масштабирую.
Код очень прост, я использую только стандартные функции CG*:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
tiledLayer.levelsOfDetail = 10;
tiledLayer.levelsOfDetailBias = 5;
tiledLayer.tileSize = CGSizeMake(512.0, 512.0);
CGMutablePathRef mutablePath = CGPathCreateMutable();
CGPathMoveToPoint(mutablePath, nil, 0, 0);
CGPathAddLineToPoint(mutablePath, nil, 700, 700);
path = CGPathCreateCopy(mutablePath);
CGPathRelease(mutablePath);
}
return self;
}
+ (Class) layerClass {
return [CATiledLayer class];
}
- (void) drawRect:(CGRect)rect {
}
- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
CGContextSetRGBFillColor(ctx, 1, 1, 1, 1);
CGContextFillRect(ctx, self.bounds);
CGContextSetLineWidth(ctx, 5);
CGContextAddPath(ctx, path);
CGContextDrawPath(ctx, kCGPathStroke);
}
- (void)dealloc {
[super dealloc];
}
ОБНОВЛЕНИЕ: я заметил, что эта проблема существует только на iOS 5, она отлично работает на 4.3
1 ответ
Я столкнулся с аналогичной проблемой при попытке рисовать кэшированные объекты CGPath в пользовательском MKOverlayView.
Сбой может произойти из-за того, что CGPath не может быть одновременно нарисован в нескольких потоках - это непрозрачный класс, который (как указано в документации) содержит указатель на текущую точку в своем массиве точек. Два или более потоков, проходящих по этому массиву одновременно, пока они рисуют, могут привести к неопределенному поведению и падению.
Я обошел это, скопировав объект CGPath в каждый поток рисования (содержащийся в блокировке мьютекса, чтобы предотвратить неполное копирование):
//lock the object's cached data
pthread_mutex_lock(&cachedPathMutex);
//get a handle on the previously-generated CGPath (myObject exists on the main thread)
CGPathRef myPath = CGPathCreateCopy(myObject.cachedPath);
//unlock the mutex once the copy finishes
pthread_mutex_unlock(&cachedPathMutex);
// all drawing code here
CGContextAddPath(context, myPath);
...
...
CGPathRelease(myPath);
Если вас беспокоит нехватка памяти при выполнении копирования в каждом потоке, вы также можете работать непосредственно с кэшированными объектами CGPath, но мьютекс должен оставаться заблокированным в течение всего процесса рисования (что отрицательно сказывается на цели многопоточности). Рисование):
//lock the object's cached data
pthread_mutex_lock(&cachedPathMutex);
//get a handle on the previously-generated CGPath (myObject exists on the main thread)
CGPathRef myPath = myObject.cachedPath;
// draw the path in the current context
CGContextAddPath(context, myPath);
...
...
//and unlock the mutex
pthread_mutex_unlock(&cachedPathMutex);
Я уточню свой ответ, сказав, что я не являюсь экспертом по многопоточному рисованию с помощью Quartz, только то, что этот подход решил сбои в моем сценарии. Удачи!
ОБНОВЛЕНИЕ: я пересмотрел этот код сейчас, когда iOS 5.1.0 вышла, и похоже, что основной причиной проблемы, возможно, была ошибка в Quartz в iOS 5.0.x. При тестировании на iOS 5.1.0 с удаленными вызовами CGPathCreateCopy() и мьютексом я не вижу ни одного сбоя, произошедшего на iOS 5.0.x.
//get a handle on the previously-generated CGPath (myObject exists on the main thread)
CGPathRef myPath = myObject.cachedPath;
// all drawing code here
CGContextAddPath(context, myPath);
...
...
//drawing finished
Поскольку есть вероятность, что мы будем поддерживать iOS 5.0.x некоторое время, не помешает сохранить мьютекс в коде (кроме небольшого снижения производительности) или просто запустить проверку версии перед рисованием.