Рисование в фоновом потоке на iOS

У меня есть представление с очень сложной логикой рисования (это представление карты, основанное на данных ГИС). Выполнение этого рисунка в основном потоке блокирует пользовательский интерфейс и делает приложение невосприимчивым. Я хочу переместить рисунок в фоновый поток, например, с помощью NSOperation.

Каков наилучший способ структурировать это?

В настоящее время я рисую в CGContext вне памяти, а затем конвертирую его в CGImageRef, который я отправляю в представление для блитирования в основном потоке. К сожалению, это использует много памяти, и кажется, что ускорение графического процессора больше не используется, поскольку оно немного медленнее. Есть ли какой-нибудь способ рисования непосредственно в представлении из фонового потока? Я знаю, что 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]];
}

Это должно сработать.

Также попробуйте попробовать свой процесс и проверить, кто потребляет ЦП. Это сделает пользовательский интерфейс отзывчивым и очень быстрым.

Проблема с производительностью, о которой вы упомянули, может быть связана с чем-то другим. Попробуйте пример процесса процессора. Это даст вам представление о том, кто на самом деле использует ЦП.

person RLT    schedule 12.10.2011
comment
Итак, я пробую этот подход сейчас, но даже если я запускаю NSTimer и запускаю только 50% времени (выполняется с интервалом 0,2 с, максимум 0,1 с за цикл рисования), кажется, что он по-прежнему игнорирует все события пользовательского интерфейса. Есть ли в этом хитрость? - 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