Срив на iOS CATiledLayer

Имам приложение за четене на pdf за iPad, където използвам scrollview за показване на всяка страница. Държа страницата в изглед и една страница от двете страни на страницата в изглед. Имам отделни изгледи за портретен и пейзажен изглед. Портретният изглед показва една страница, а пейзажният изглед показва 2 страници.

Когато iPad промени ориентацията, разтоварвам изгледа за старата ориентация и зареждам изгледа за новата ориентация. Да кажем, че е бил в портретен изглед и след това се променя на пейзажен, приложението разтоварва портретния изглед и зарежда пейзажния. Всичко това работи чудесно, освен когато pdf файловете са големи.

PDF файловете са начертани с помощта на tiledlayers. Приложението се срива, когато ориентацията се промени с големи pdf файлове. Приложението се срива само ако ориентацията се промени, преди всички плочки да бъдат начертани. Предполагам, че се срива, защото се опитва да начертае плочки към изглед, отколкото е бил разтоварен. И така, има ли начин да спра рисуването на плочки, когато разтоваря изгледа?


person booboo-a-choo    schedule 16.05.2011    source източник
comment
Не намерих подходящо решение за това, но заобиколното решение предотврати сривове. В крайна сметка поставих if (self.tiling == YES && [self.view superview] != nil) в - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx метод. Самостоятелното подреждане е зададено преди изгледът да бъде премахнат от superview или във вашия случай, преди промяната на ориентацията. Можете да избегнете този проблем, като имате един изглед за всички ориентации. Моето заобиколно решение предотврати сривове, но не попречи на CATiledLayer да получи достъп до изображение или да задържи заключване върху изображение по някакъв мистериозен начин. Крайният резултат беше невъзможност за изтриване на споменатото изображение от файловата система. Късмет!   -  person TheBlack    schedule 16.05.2011


Отговори (2)


Трябва да настроите делегата на CALayer на нула, след което да го премахнете от суперизгледа. Това спира изобразяването, след което можете безопасно да освободите.

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

Освен това не забравяйте да извикате това от основната нишка, в противен случай ще ви очакват ужасни грешки.

person steipete    schedule 01.08.2011
comment
1) Сигурни ли сте, че [self.layer removeFromSuperlayer]; е задължително? 2) Фактът, че вашето задържане/освобождаване помага срещу сривове, не сочи ли факта, че имате състояние на състезание, което все още може да се случи? Запазването предпазва от извикване на dealloc (в резултат на освобождаване в друга нишка), но dealloc все още може да възникне между началото на функцията до извикването на retain и в крайна сметка ще извикате retain на освободена памет. - person Danra; 14.11.2011
comment
Също така открих, че трябва да премахнете всички подизгледи преди да зададете self.layer.delegate nil, в противен случай те няма да бъдат освободени. - person Danra; 14.11.2011
comment
Прав сте, запазването/освобождаването не е необходимо - актуализирах отговора си. - person steipete; 15.11.2011
comment
Вие, сър, сте красиви (да не говорим за красотата на PSPDFKit също =P). - person fumoboy007; 02.04.2013

Не съм гледал разглобяването, за да видя, но използваме малко по-различно решение. Задаването на свойството 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 е безопасно.

Ако някой има предложения за подобрение, моля да ме уведомите.

person russbishop    schedule 01.12.2014