2010-10-27 2 views
4

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

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

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

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

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

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

ответ

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

UPDATE: Насколько я помню, там были проблемы с этим в более старых версиях прошивки, когда он пришел к CATiledLayers, но установка делегата на ноль перед тем dealloc теперь путь. См.: https://stackoverflow.com/a/4943231/2882

+0

Это отлично работает для меня, просто и эффективно. –

+4

Если представление уже освобождено, как работает self.superview (доступ к себе, освобожденный объект)? –

3

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

+0

Я уже делаю это. Благодарю. – TomSwift

+0

Примечание: комментарий TomSwift касается отказа делегата в dealloc. Это действительно слишком поздно, и ответ был отредактирован до нулевого делегата * до того, как он выпустил представление. – Danra

9

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

+1

Питер прав, это единственный надежный способ сделать это. Он не задокументирован, но я написал тест, чтобы подтвердить, что он действительно блокируется до тех пор, пока не будут завершены все вызовы делегатов. Установка делегата на nil (принятый ответ выше) ничего не делает, если задачи рисования уже поставлены в очередь в GCD. – Crufty

0

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

Смежные вопросы