Рисуване във фонова нишка на iOS

Имам изглед с много сложна логика на чертане (това е изглед на карта, който черпи от GIS данни). Извършването на този чертеж в основната нишка заключва потребителския интерфейс и прави приложението блокирано. Искам да преместя чертежа във фонова нишка с например NSOperation.

Какъв е най-добрият начин да структурирате това?

В момента рисувам в CGContext без памет и след това го конвертирам в CGImageRef, който изпращам на изгледа, за да го изтрия в основната нишка. За съжаление това изразходва много памет и изглежда, че GPU ускорението вече не се използва, тъй като е доста по-бавно. Има ли някакъв начин за рисуване директно към изгледа от фонова нишка? Знам, че UIKit не е многопоточно безопасен, но може би има някакъв начин за заключване на изгледа, докато правя чертежа?


person Jon Tirsen    schedule 09.10.2011    source източник


Отговори (2)


Никога не съм попадал на такава ситуация на iPhone, но на Mac имах подобен проблем веднъж.

  1. Използвайте CGLayer, за да делегирате дейност по рисуване на офлайн контексти.
  2. Опитайте да добавите таймер за текущия NSRunLoop и на времеви интервал изпълнете вашите графични команди. Трябва да изглежда нещо подобно...

...

kRenderFPS 25.0 //This is Maximum value

renderTimer = [[NSTimer timerWithTimeInterval:(1.0 / (NSTimeInterval)kRenderFPS) target:viewobject selector:@selector(RenderUI:) userInfo:nil repeats:YES] retain];


[[NSRunLoop currentRunLoop] addTimer:renderTimer forMode:NSDefaultRunLoopMode];


[[NSRunLoop currentRunLoop] addTimer:renderTimer forMode:NSModalPanelRunLoopMode];


[[NSRunLoop currentRunLoop] addTimer:renderTimer forMode:NSEventTrackingRunLoopMode];

//In view class
-(void)RenderUI:(id)param
{
    [self setNeedsDisplayInRect:[self bounds]];
}

Това трябва да свърши работа.

Също така опитайте да вземете проба от вашия процес и проверете кой консумира процесора. Това ще направи потребителския интерфейс отзивчив и много бърз.

Проблемът с производителността, който споменахте, може да се дължи на нещо друго. Опитайте се да направите примерен процес на процесора. Това ще ви даде представа кой всъщност взема CPU.

person RLT    schedule 12.10.2011
comment
comment
Така че сега опитвам този подход, но дори ако стартирам с NSTimer и стартирам само 50% от времето (изпълнявам на интервали от 0,2 s за максимум 0,1 s на цикъл на чертане), изглежда, че все още игнорира всички събития от потребителския интерфейс. Има ли трик за това? - person Jon Tirsen; 09.11.2011
comment
Кой стил пробвахте? CGLayer или NSRunLoop или и двете? Използвайте CGLayer, за да делегирате чертане извън екрана, и NSRunloop, за да изпълнявате команди за чертане във времеви интервал. - person RLT; 09.11.2011
comment
Забелязвам, че някой гласува против отговора ми, но не даде никакво обяснение защо -1. Не е готино. - person RLT; 09.11.2011
comment
Използвах както CGLayer, така и NSRunLoop. Сега опитвам CGLayer с NSOperation (т.е. във фонова нишка). Получавам доста ужасно представяне с фоновата нишка обаче. (Въпреки че потребителският интерфейс вече реагира.) - person Jon Tirsen; 09.11.2011
comment
Ще добавя повече подробности към отговора си. - person RLT; 09.11.2011
comment
Каква е ползата от използването на CGLayer над CGBitmapContext? Когато създавате своя CGLayer, използвате ли UIGraphicsCurrentContext от drawRect или създавате CGBitmapContext и след това създавате CGLayer въз основа на това? - person Jon Tirsen; 09.11.2011
comment
Използвайте CGBitmapContext само когато трябва да създадете растерно изображение от него. Ако е офлайн контекст, използвайте CGLayer. CGLayer обектите са полезни за чертане извън екрана и могат да се използват почти по същия начин, по който може да се използва контекст на растерно изображение. Всъщност един CGLayer обект е много по-добро представяне от растерния контекст. - person RLT; 09.11.2011
comment
Използването на CGLayer обекти може да подобри производителността, особено когато трябва да заснемете част от чертеж, който щамповате многократно (използвайки същия мащабен фактор и ориентация). Quartz може да кешира CGLayer обекти към видеокартата, което прави изчертаването на CGLayer до дестинация много по-бързо от изобразяването на еквивалентно изображение, изградено от контекст на растерно изображение. - person RLT; 09.11.2011
comment
Добре, грешката беше моя, извиквах setNeedsDisplay във фоновата нишка. Може би някой с достатъчно привилегии може да изчисти тези коментари. :-) - person Jon Tirsen; 09.11.2011
comment
@Rahul Премахнах своя -1 глас, откакто редактирахте отговора си. По това време дори не беше отговор. - person Dan Rosenstark; 14.11.2011
comment
@Yar- Нямам нищо против да гласувам. Причината за отрицателен вот би била добра, така че отговорът да може да бъде актуализиран, ако нещо не е ясно. - person RLT; 14.11.2011

Отвъд iOS 4.0, рисуването е безопасно за нишки. Може да се наложи да създадете CGContext сами, но няма причина да не можете да рисувате върху фонова нишка.

Въпреки това повечето UIKit операции не са. Ако трябва да направите това, винаги можете да се подготвите във фонова нишка и след това да използвате performOnMainThread, когато е необходимо. Има дори waitUntilDone. Това каза, че трябва да използвате NSOperation и NSOperationQueue, очевидно.

person Dan Rosenstark    schedule 11.10.2011
comment
Изглежда има много объркване относно това, но това е правилният отговор, тъй като от iOS 4.0 рисуването с UIKit е безопасно за нишки. Ето официалното съобщение за това: developer.apple .com/library/ios/#releasenotes/General/ (търсене на нишка). - person Jon Tirsen; 31.10.2011