2013-03-19 2 views
1

Я показываю информацию из модели данных в пользовательском интерфейсе. Мой текущий подход к выполнению так с помощью делегации следующим образом:Что считается перегрузкой основной нити?

@protocol DataModelDelegate <NSObject> 
- (void)updateUIFromDataModel; 
@end 

Я реализую метод делегата в моем классе контроллера следующим образом, используя НОД, чтобы подтолкнуть обновление пользовательского интерфейса к главной теме:

- (void)updateUIFromDataModel { 

    dispatch_async(dispatch_get_main_queue(), ^{ 

     // Code to update various UI controllers 
     // ... 
     // ... 

    }); 
} 

Что меня беспокоит, так это то, что в некоторых ситуациях этот метод можно назвать очень часто (~ 1000 раз в секунду, при каждом обновлении нескольких объектов пользовательского интерфейса), что для меня очень похоже на то, что я «рассылаю спам» основной поток с командами.

Это слишком много для отправки в основной поток? Если это так, у кого-нибудь есть идеи о том, что было бы лучшим способом приблизиться к этому?

Я искал dispatch_apply, но это кажется более полезным при объединении данных, что не то, что мне нужно - я просто хочу пропустить обновления, если они слишком часты, поэтому отправляется только разумное количество обновлений к основной теме!

Я рассматривал возможность использования другого подхода и использования таймера вместо этого для постоянного опроса данных, скажем каждые 10 мс, однако, поскольку обновление данных имеет тенденцию быть спорадическим, я чувствую, что это было бы расточительно.

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

редактировать:Добавлен ответ ниже показаны приспособления с использованием источника отправки

ответ

7

Один из вариантов заключается в использовании Dispatch Source с типом DISPATCH_SOURCE_TYPE_DATA_OR, который позволяет вам публиковать события неоднократно, и libdispatch объединяет их для вас. Когда у вас есть что-то для публикации, вы используете dispatch_source_merge_data, чтобы он знал, что есть что-то новое. Несколько вызовов на dispatch_source_merge_data будут объединены вместе, если целевая очередь (в вашем случае, основная очередь) занята.

+0

Я взглянул на это, но немного запутался в том, как его реализовать. При создании настраиваемого источника отправки представляется единственными параметрами для объединения данных с использованием логического ИЛИ или логического ИЛИ, но то, что я хочу получить самое последнее значение, которое было отправлено только, и отменить все вызовы, которые поставили в очередь из-за загруженного основного потока? – tom1990

+0

Если все, что вам нужно, самое последнее значение, вы можете просто сохранить это где-то еще (например, глобальное) и перезаписать его (поточно-безопасным способом!) Каждый раз, когда вы отправляете новое значение. Затем вы можете прочитать это в своем основном потоке, который будет последним значением. –

+0

Ahh, я понял - так использую источник отправки как средство объединения обновлений уведомлений, когда вещи становятся слишком занятыми и игнорируют значение, которое оно дает, и просто используйте блок, который отвечает на чтение из другого места? Я об этом не думал. Я полагаю, что в этом отношении я мог бы фактически использовать «DISPATCH_SOURCE_TYPE_DATA_ADD» и добавлять по одному для каждого отправленного сообщения - тогда результирующее значение могло бы использоваться для контроля того, сколько сообщений пропускается из-за того, что основной поток слишком занят, чтобы обрабатывать их все. Спасибо за помощь! – tom1990

2

Если приложение пляжными мячами под большой нагрузкой, то вы заблокировали основной поток слишком долго, и вы должны внедрить стратегию объединения для обновлений пользовательского интерфейса. Если приложение по-прежнему реагирует на клики, а не на пляже, тогда все в порядке.

+0

Привет, спасибо за ваш ответ, когда он занят, это не пляжный мяч, но производительность, похоже, немного повлияла. Пользовательский интерфейс остается отзывчивым, но все так слегка задерживается, поэтому я думаю, что я близок к блокировке основного потока при тяжелой загрузке данных. – tom1990

+0

Стратегия коалесценции, которая может работать для вас, заключается в использовании двух разных таймеров. Установите и сбросьте таймер 1 каждый раз, когда данные будут изменены. Не обновляйте представление до тех пор, пока не запустится этот таймер. Таймер 2 должен следить за тем, чтобы данные обновлялись по крайней мере каждые n секунд (если они менялись) в случае, если Таймер 1 никогда не имеет возможности запускать. –

+0

Если в какой-то момент мне нужно постоянное обновление, я думаю, что так я поеду, мне нравится, как это может обеспечить как максимальную скорость обновления короткого пакета, так и максимальное время между обновлениями для медленных периодов. Если я решил использовать это вы бы предположили добавить флаг 'BOOL' к чему-то по строкам 'viewUpdateRequested' (который может быть установлен методом делегата), а затем для методов' onTick: 'таймера проверить, установлен ли этот флаг перед выполнением Обновить? – tom1990

5

Я экспериментировал с источниками отправки и получил это работает, как ожидается, сейчас - Вот как я адаптировали свою реализацию класса в случае использования для всех, кто попадается этот вопрос:

@implementation AppController { 
@private 
    dispatch_source_t _gcdUpdateUI; 
} 

- (void)awakeFromNib { 

    // Added the following code to set up the dispatch source event handler: 

    _gcdUpdateUI = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, 
              dispatch_get_main_queue()); 
    dispatch_source_set_event_handler(_gcdUpdateUI, ^{ 

     // For each UI element I want to update, pull data from model object: 
     // For testing purposes - print out a notification: 
     printf("Data Received. Messages Passed: %ld\n", 
       dispatch_source_get_data(_gcdUpdateUI)); 
    }); 

dispatch_resume(_gcdUpdateUI); 

} 

И Теперь в методе делегата я снял вызов dispatch_async, и заменить его следующим:

- (void)updateUIFromDataModel { 

    dispatch_source_merge_data(_gcdUpdateUI, 1); 

} 

Это работает абсолютно нормально для меня. Теперь даже при самых интенсивных обновлениях данных пользовательский интерфейс остается отлично реагирующим.

Несмотря на то, что выход printf() был очень грубым способом проверки того, работает ли коалесцирование, быстрый прокрутка на консольном выходе показала мне, что большая часть распечаток сообщений имеет значение 1 (легко 98% из них) , однако были прерывистые переходы примерно до 10-20, достигнув пикового значения чуть более 100 коалесцированных сообщений за время, когда модель отправляла большинство сообщений об обновлении.

Еще раз спасибо за помощь!

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