setNeedsDisplayInRect вызывается во время drawLayer: inContext: - PullRequest
5 голосов
/ 16 ноября 2011

Я использую CATiledLayer в своем приложении, и в результате рисование этого слоя выполняется в фоновом потоке. То есть метод drawLayer: inContext: моего делегата вызывается из фонового потока. SetNeedsDisplayInRect, используемый для аннулирования частей CATiledLayer, всегда вызывается из основного потока.

Поскольку они являются независимыми потоками, иногда случается, что setNeedsDisplayInRect вызывается, когда фоновый поток находится в методе drawLayer: inContext. Я заметил, что setNeedsDisplayInRect игнорируется в этой ситуации (drawLayer: inContext больше не вызывается).

Я зарегистрировал ошибку в Apple , потому что я думаю, что это не правильно. Но мне трудно понять, как обойти эту ситуацию. У вас есть хорошие идеи?

EDIT:

Я проверил ответ Станислава, используя следующий код:

- (void) setNeedsDisplayInRect:(CGRect)rect
{
    NSLog(@"setNeedsDisplayInRect:%@", NSStringFromCGRect(rect));
    [super setNeedsDisplayInRect:rect];
}

- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)gc
{
    CGRect bounds = CGContextGetClipBoundingBox(gc);
    NSLog(@"drawLayer:inContext: bounds=%@", NSStringFromCGRect(bounds));

    dispatch_async(dispatch_get_current_queue(), ^{
        [self setNeedsDisplayInRect:bounds];
    });

    CGContextSetFillColorWithColor(gc, testColor.CGColor);
    CGContextFillRect(gc, bounds);
    sleep(0.2); // simulate the time it takes to draw complicated graphics
    NSLog(@"end drawLayer:inContext: bounds=%@", NSStringFromCGRect(bounds));
}

Как указано, код заставляет рисование повторяться бесконечно, но иногда существует задержка до 5 секунд между setNeedsDisplayInRect: и соответствующим drawLayer: inContext :, в котором больше ничего не происходит. Смотрите журнал ниже в качестве примера. Обратите внимание на нерегулярное поведение: в первую секунду некоторые плитки перерисовываются несколько раз, другие только один раз. Затем наступает пауза в 5 секунд, и цикл начинается снова.

Это было протестировано на симуляторе с IOS6.0 (я выбираю эту версию, потому что в более ранних версиях есть еще одна ошибка, исправленная в 6.0: иногда они рисуют одни и те же плитки дважды).

2012-10-27 15:51:38.771 TiledLayerTest[39934:15a13] drawLayer:inContext: bounds={{0, 300}, {300, 180}}
2012-10-27 15:51:38.774 TiledLayerTest[39934:15a13] end drawLayer:inContext: bounds={{0, 300}, {300, 180}}
2012-10-27 15:51:38.774 TiledLayerTest[39934:1570f] drawLayer:inContext: bounds={{300, 0}, {20, 300}}
2012-10-27 15:51:38.776 TiledLayerTest[39934:1570f] end drawLayer:inContext: bounds={{300, 0}, {20, 300}}
2012-10-27 15:51:38.776 TiledLayerTest[39934:1630b] setNeedsDisplayInRect:{{0, 300}, {300, 180}}
2012-10-27 15:51:38.777 TiledLayerTest[39934:1540f] setNeedsDisplayInRect:{{300, 0}, {20, 300}}
2012-10-27 15:51:38.780 TiledLayerTest[39934:15a13] drawLayer:inContext: bounds={{300, 0}, {20, 300}}
2012-10-27 15:51:38.781 TiledLayerTest[39934:15a13] end drawLayer:inContext: bounds={{300, 0}, {20, 300}}
2012-10-27 15:51:38.782 TiledLayerTest[39934:1540f] setNeedsDisplayInRect:{{300, 0}, {20, 300}}
2012-10-27 15:51:38.789 TiledLayerTest[39934:1570f] drawLayer:inContext: bounds={{0, 0}, {300, 300}}
2012-10-27 15:51:38.791 TiledLayerTest[39934:15a13] drawLayer:inContext: bounds={{300, 300}, {20, 180}}
2012-10-27 15:51:38.792 TiledLayerTest[39934:15a13] end drawLayer:inContext: bounds={{300, 300}, {20, 180}}
2012-10-27 15:51:38.793 TiledLayerTest[39934:1570f] end drawLayer:inContext: bounds={{0, 0}, {300, 300}}
2012-10-27 15:51:38.795 TiledLayerTest[39934:1540f] setNeedsDisplayInRect:{{0, 0}, {300, 300}}
2012-10-27 15:51:38.795 TiledLayerTest[39934:1540f] setNeedsDisplayInRect:{{300, 300}, {20, 180}}
2012-10-27 15:51:38.798 TiledLayerTest[39934:15a13] drawLayer:inContext: bounds={{0, 0}, {300, 300}}
2012-10-27 15:51:38.800 TiledLayerTest[39934:15a13] end drawLayer:inContext: bounds={{0, 0}, {300, 300}}
2012-10-27 15:51:38.802 TiledLayerTest[39934:1630b] setNeedsDisplayInRect:{{0, 0}, {300, 300}}
2012-10-27 15:51:38.806 TiledLayerTest[39934:1570f] drawLayer:inContext: bounds={{0, 300}, {300, 180}}
2012-10-27 15:51:38.808 TiledLayerTest[39934:1630b] setNeedsDisplayInRect:{{0, 300}, {300, 180}}
2012-10-27 15:51:38.809 TiledLayerTest[39934:1570f] end drawLayer:inContext: bounds={{0, 300}, {300, 180}}
2012-10-27 15:51:38.813 TiledLayerTest[39934:15a13] drawLayer:inContext: bounds={{0, 300}, {300, 180}}
2012-10-27 15:51:38.816 TiledLayerTest[39934:1630b] setNeedsDisplayInRect:{{0, 300}, {300, 180}}
2012-10-27 15:51:38.816 TiledLayerTest[39934:15a13] end drawLayer:inContext: bounds={{0, 300}, {300, 180}}
2012-10-27 15:51:43.774 TiledLayerTest[39934:1540f] drawLayer:inContext: bounds={{0, 300}, {300, 180}}
2012-10-27 15:51:43.776 TiledLayerTest[39934:1540f] end drawLayer:inContext: bounds={{0, 300}, {300, 180}}
2012-10-27 15:51:43.776 TiledLayerTest[39934:1630f] drawLayer:inContext: bounds={{0, 0}, {300, 300}}

Ответы [ 2 ]

0 голосов
/ 27 октября 2012

Вы можете попробовать использовать NSRecursiveLock следующим образом:

- (void) setNeedsDisplayInRect:(CGRect)rect
{
    [self.lock lock]
    NSLog(@"setNeedsDisplayInRect:%@", NSStringFromCGRect(rect));
    [super setNeedsDisplayInRect:rect];
    [self.lock unlock]
}

- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)gc
{
    [self.lock lock]
    CGRect bounds = CGContextGetClipBoundingBox(gc);
    NSLog(@"drawLayer:inContext: bounds=%@", NSStringFromCGRect(bounds));


    // drawing code


    NSLog(@"end drawLayer:inContext: bounds=%@", NSStringFromCGRect(bounds));
    [self.lock unlock]
}

Это гарантирует, что setNeedsDisplayInRect не вызывается во время рисования.Однако это может повлиять на производительность, поскольку lock может блокировать основной поток, и вы не можете рисовать несколько плиток параллельно.

0 голосов
/ 27 октября 2012

Я отправил свой ответ на похожую проблему: setNeedsDisplayInMapRect не вызывает новый drawMapRect: вызов (просто ссылка, чтобы не дублировать ответ здесь).

Коротко: вы должны отправить вызов метода setNeedsDisplayInRect в dispatch_get_main_queue ().

...