Grand Central Dispatch (GCD) и executeSelector - нужно лучшее объяснение
Я использовал и GCD, и executeSelectorOnMainThread:waitUntilDone в своих приложениях и склонен считать их взаимозаменяемыми - то есть, executeSelectorOnMainThread:waitUntilDone - это оболочка Obj-C для синтаксиса GCD C. Я думал об этих двух командах как об эквивалентных:
dispatch_sync(dispatch_get_main_queue(), ^{ [self doit:YES]; });
[self performSelectorOnMainThread:@selector(doit:) withObject:YES waitUntilDone:YES];
Я не прав? То есть, есть ли отличие команд executeSelector* от команд GCD? Я прочитал много документации по ним, но до сих пор не нашел окончательного ответа.
3 ответа
performSelectorOnMainThread:
не использует GCD для отправки сообщений объектам в главном потоке.
Вот как в документации сказано, что метод реализован:
- (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 в цикле выполнения текущего потока в начале следующей итерации цикла выполнения. Таймер настроен для работы в режимах, указанных параметром modes. Когда таймер срабатывает, поток пытается удалить сообщение из цикла выполнения и выполнить селектор. Это успешно, если цикл выполнения работает и в одном из указанных режимов; в противном случае таймер ожидает, пока цикл выполнения не перейдет в один из этих режимов.
Как указывает Иаков, хотя они могут казаться одинаковыми, это разные вещи. На самом деле, существует существенная разница в том, как они обрабатывают отправку действий в основной поток, если вы уже работаете в основном потоке.
Я столкнулся с этим недавно, где у меня был общий метод, который иногда запускался из чего-то в главном потоке, иногда нет. Чтобы защитить определенные обновления пользовательского интерфейса, я использовал -performSelectorOnMainThread:
для них без проблем.
Когда я переключился на использование dispatch_sync
в основной очереди приложение блокировалось всякий раз, когда этот метод выполнялся в основной очереди. Чтение документации по dispatch_sync
, мы видим:
Вызов этой функции и ориентация на текущую очередь приводят к тупику.
где для -performSelectorOnMainThread:
мы видим
Подождите
Логическое значение, которое указывает, блокируется ли текущий поток до тех пор, пока указанный приемник не будет выполнен на приемнике в основном потоке. Укажите ДА, чтобы заблокировать эту тему; в противном случае укажите NO, чтобы этот метод возвращался немедленно.
Если текущий поток также является основным, и вы указываете ДА для этого параметра, сообщение доставляется и обрабатывается немедленно.
Я все еще предпочитаю элегантность 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
});
чтобы выполнить действия, которые мне нужно защитить в главном потоке, не беспокоясь о том, в каком потоке был выполнен исходный метод.
Предполагается, что способ GCD более эффективен и прост в обращении, и он доступен только в iOS4 и выше, тогда как executeSelector поддерживается в более старых и новых версиях iOS.