CATiledLayer drawInContext вызывается после удаления связанного представления

Сегодня я столкнулся с интересной проблемой iOS, связанной с CATiledLayer. Это произошло только на устройстве, а не в симуляторе.

Мое представление привлекает его CALayer через обратный вызов делегата drawLayer: inContext:. Этот уровень имеет подуровень, производный от CATiledLayer, который выполняет собственное рисование в переопределенном методе drawInContext :.

Оба уровня отображают содержимое PDF-файла с помощью CGContextDrawPDFPage (). (CALayer рисует версию с низким разрешением, а подслой CATiledLayer рисует содержимое с высоким разрешением поверх.)

Я столкнулся со сценарием, в котором я бы закончил с представлением - удалил его из своего супервизора и выпустил его. dealloc () вызывается в представлении. Некоторое время спустя метод drawInContext: CATiledLayer будет вызываться (в фоновом потоке) системой. Он будет рисовать, но при возврате из метода Springboard выйдет из строя, и при этом мое приложение также отключится.

Я исправил это, установив флаг в CATiledLayer, сообщив ему больше не отображать, из метода dealloc представления.

Но я могу только представить, что есть более элегантный способ. Почему метод CATiledLayer drawInContext: все еще вызывается после родительского уровня, а представление родительского уровня освобождается? Как правильно закрыть представление, чтобы этого не произошло?


person TomSwift    schedule 27.10.2010    source источник


Ответы (4)


-(void)drawLayer:(CALayer *)calayer inContext:(CGContextRef)context {    
   if(!self.superview)
      return;
   ...

ОБНОВЛЕНИЕ: Насколько я помню, в старых версиях iOS с этим были проблемы, когда дело доходило до CATiledLayers, но теперь можно использовать установку делегата на nil перед тем, как dealloc. См .: https://stackoverflow.com/a/4943231/2882

person John Lemberger    schedule 31.03.2011
comment
Если представление уже освобождено, как может работать self.superview (доступ к себе, освобожденный объект)? - person Vladimir Gritsenko; 11.09.2012

Медленный, но лучший способ исправить это - также установить view.layer.contents = nil. Это ждет завершения потоков.

person steipete    schedule 12.08.2011
comment
Петр прав, это единственный надежный способ сделать это. Это не задокументировано, но я написал тест, чтобы подтвердить, что он действительно блокируется, пока не завершатся все фоновые вызовы делегатов. Установка делегата на nil (принятый ответ выше) ничего не делает, если задачи рисования уже поставлены в очередь в GCD. - person Crufty; 06.12.2013

Установите view.layer.delegate в ноль перед освобождением представления.

person Danra    schedule 09.02.2011
comment
Примечание: комментарий TomSwift касается обнуления делегата в dealloc. Это действительно слишком поздно, и ответ был отредактирован, чтобы обнулить делегата перед освобождением представления. - person Danra; 12.09.2012

На это потратил довольно много времени. Мой последний подход - объявить блочную переменную и присвоить ей значение self в методе viewWillDisappear. Затем вызовите вызов setContents в глобальной очереди отправки - нет необходимости блокировать основной поток. Затем, когда setContents вернется, вызовите обратно в основной поток и установите для переменной блока значение nil, что должно гарантировать, что контроллер представления будет освобожден в основном потоке. Однако одно предостережение: я обнаружил, что разумно использовать dispatch_after для вызова в основной поток, поскольку глобальная очередь отправки сохраняет контроллер представления до тех пор, пока он не выйдет из своего блока, что означает, что у вас может быть состояние гонки между его выходом (и освобождением контроллер представления) и блок основного потока, устанавливающий для переменной блока значение nil), что может привести к освобождению глобального потока очереди отправки.

person Rohinton Collins    schedule 10.05.2013