Grand Central Dispatch (GCD) против performSelector - требуется лучшее объяснение
Я использовал как GCD, так и performSelectorOnMainThread: waitUntilDone в своих приложениях и, как правило, считаю их взаимозаменяемыми - то есть performSelectorOnMainThread: waitUntilDone - обертка Obj-C для синтаксиса GCD C. Я думал об этих двух командах как эквивалент:
dispatch_sync(dispatch_get_main_queue(), ^{ [self doit:YES]; });
[self performSelectorOnMainThread:@selector(doit:) withObject:YES waitUntilDone:YES];
Я неверен? То есть, существует ли разница в исполнении команд 'Selectlect *' и GCD? Я прочитал много документации по ним, но еще не нашел окончательного ответа.
Ответы
Ответ 1
performSelectorOnMainThread:
использует не GCD для отправки сообщений объектам в основном потоке.
Вот как documentation говорит, что метод реализован:
- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait {
[[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
}
И на performSelector:target:withObject:order:modes:
в документации указано:
Этот метод устанавливает таймер для выполнения сообщения aSelector в текущем цикле выполнения потоков в начале следующей итерации цикла запуска. Таймер сконфигурирован для работы в режимах, указанных параметром режимов. Когда таймер срабатывает, поток пытается удалить сообщение из цикла запуска и выполнить селектор. Это успешно, если цикл запуска запущен и в одном из указанных режимов; в противном случае таймер ждет, пока цикл выполнения не окажется в одном из этих режимов.
Ответ 2
Как указывает Джейкоб, хотя они могут казаться одинаковыми, они разные. На самом деле существует значительная разница в том, как они обрабатывают отправку действий в основной поток, если вы уже используете основной поток.
Я столкнулся с этим недавно, когда у меня был общий метод, который иногда запускался из чего-то в основном потоке, иногда нет. Чтобы защитить определенные обновления пользовательского интерфейса, я использовал -performSelectorOnMainThread:
для них без проблем.
Когда я переключился на использование dispatch_sync
в основной очереди, приложение заработало бы в тупике всякий раз, когда этот метод запускался в основной очереди. Читая документацию на dispatch_sync
, мы видим:
Вызов этой функции и таргетинга текущая очередь приводит к тупиковой ситуации.
где для -performSelectorOnMainThread:
мы видим
ожидание
Логическое значение, которое указывает, текущие блоки потока до тех пор, пока указанный селектор выполняется на приемник на основной нити. Указывать ДА, чтобы заблокировать эту тему; в противном случае, укажите NO, чтобы этот метод возвращался немедленно.
Если текущий поток также является основным нить, и вы указываете YES для этого параметр, сообщение доставляется и обрабатывается немедленно.
Я по-прежнему предпочитаю элегантность GCD, лучшую проверку времени компиляции и большую гибкость в отношении аргументов и т.д., поэтому я сделал эту небольшую вспомогательную функцию для предотвращения взаимоблокировок:
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
Обновление: В ответ на Дэйв Дрибин, указав раздел оговорки в dispatch_get_current_queue()
, я изменил к использованию [NSThread isMainThread]
в приведенном выше коде.
Затем я использую
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
выполнить действия, которые необходимо защитить на основном потоке, не беспокоясь о том, в какой поток был выполнен исходный метод.
Ответ 3
GCD-способ, по-видимому, более эффективен и прост в обращении и доступен только в iOS4, тогда как performSelector поддерживается в более старой и новой iOS.