Сбой iOS CATiledLayer - PullRequest
       6

Сбой iOS CATiledLayer

3 голосов
/ 16 мая 2011

У меня есть приложение для чтения PDF для iPad, где я использую прокрутки для отображения каждой страницы. Я держу страницу в поле зрения и одну страницу с каждой стороны страницы в поле зрения. У меня есть отдельные виды для портретных и пейзажных видов. В книжной ориентации отображается одна страница, а в альбомной - 2 страницы.

Когда iPad меняет ориентацию, я выгружаю вид для старой ориентации и загружаю вид для новой ориентации. Допустим, это было в книжной ориентации, а затем изменения в альбомной ориентации: приложение выгружает книжную ориентацию и загружает альбомную ориентацию Все это прекрасно работает, за исключением случаев, когда PDF большие.

PDF-файлы нарисованы с использованием плиточных слоев. Приложение ломается, когда ориентация изменяется с большими PDF. Приложение вылетает, только если ориентация изменена до того, как все плитки прорисованы. Я предполагаю, что это происходит сбой, потому что он пытается нарисовать плитки на вид, который был выгружен. Так есть ли способ остановить рисование плиток, когда я выгружаю вид?

Ответы [ 2 ]

4 голосов
/ 02 августа 2011

Вам нужно установить для делегата CALayer значение nil, а затем удалить его из суперпредставления. Это останавливает рендеринг, после чего вы можете безопасно удалить.

- (void)stopTiledRenderingAndRemoveFromSuperlayer; {
    ((CATiledLayer *)[self layer]).delegate = nil;    
    [self removeFromSuperview];
    [self.layer removeFromSuperlayer];
}

Кроме того, не забудьте позвонить из главного потока, иначе вас будут ждать ужасные ошибки.

3 голосов
/ 01 декабря 2014

Я не смотрел на разборку, чтобы увидеть, но мы используем немного другое решение. Установка для свойства CATiledLayer.content блоков nil и принудительное завершение всех блоков рендеринга. Это может быть безопасно перенесено в фоновый поток, затем освобождение UIView может быть возвращено в основной поток, чтобы позволить представлению и слою освободиться.

Вот один пример реализации UIViewController dealloc, которая будет поддерживать ваше CATiledLayer представление, живущее достаточно долго, чтобы безопасно остановить рендеринг, не блокируя основной поток.

- (void)dealloc
{
    // This works around a bug where the CATiledLayer background drawing 
    // delegate may still have dispatched blocks awaiting rendering after
    // the view hierarchy is dead, causing a message to a zombie object.
    // We'll hold on to tiledView, flush the dispatch queue, 
    // then let go of fastViewer.
    MyTiledView *tiledView = self.tiledView;
    if(tiledView) {
        dispatch_background(^{
            // This blocks while CATiledLayer flushes out its queued render blocks.
            tiledView.layer.contents = nil;

            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                // Make sure tiledView survives until now.
                tiledView.layer.delegate = nil;
            });
        });
    }
}

Это предположение , но некоторые из каркасов / классов Apple (StoreKit, CATiledLayer, UIGestureRecognizer) утверждают, что имеют реализации @property (weak) id delegate, но явно не обрабатывают делегат weak должным образом. Глядя на некоторые разборки, они делают решительно проверенные if != nil проверки, затем непосредственно касаются слабого свойства. Правильный способ - объявить __strong Type *delegate = self.delegate, который либо будет успешным и даст вам надежную ссылку, гарантированно выжившую, либо будет nil, но он определенно не даст вам ссылку на зомби объект (я предполагаю, что код платформы не был обновлен до ARC).

Под капотом CATiledLayer создает очередь отправки для фонового рендеринга и, по-видимому, либо небрежно касается свойства делегата, либо получает локальную ссылку, но не делает ее сильной. В любом случае, отправленные блоки рендеринга с радостью сообщат объект-зомби, если делегат будет освобожден. Недостаточно просто очистить делегата - это уменьшит количество сбоев, но не устранит их безопасно.

Установка content = nil делает dispatch_wait и блокирует, пока все существующие блоки рендеринга не будут завершены. Мы возвращаемся к основному потоку, чтобы убедиться, что dealloc безопасен.

Если у кого-то есть предложения по улучшению, пожалуйста, дайте мне знать.

...