Сбой CATiledLayer при рисовании предварительно подготовленного CGPath - PullRequest
1 голос
/ 16 декабря 2011

Я использую 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 Ответ

5 голосов
/ 15 января 2012

Я столкнулся с подобной проблемой при попытке нарисовать кэшированные объекты 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, не помешает сохранить мьютекс в вашем коде (кроме небольшого снижения производительности) или просто запуститьпроверка версии перед нанесением.

...