Grand Central Dispatch (GCD) и executeSelector - нужно лучшее объяснение - PullRequest
47 голосов
/ 07 марта 2011

Я использовал как 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 ]

66 голосов
/ 08 марта 2011

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

Я столкнулся с этим недавно, когда у меня был общий метод, который иногда запускался из чего-то в главном потоке, иногда нет. Чтобы защитить определенные обновления пользовательского интерфейса, я использовал для них -performSelectorOnMainThread: без проблем.

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

Вызов этой функции и ориентация текущая очередь приводит к тупику.

где для -performSelectorOnMainThread: мы видим

ждать

Булево значение, которое указывает, является ли текущий поток блокируется до указанный селектор выполняется на ресивер на основной ветке. Уточнить ДА, чтобы заблокировать эту тему; иначе, укажите НЕТ, чтобы этот метод возвращал немедленно.

Если текущий поток также является основным нить, и вы указываете ДА для этого параметр, сообщение доставлено и обработано немедленно.

Я все еще предпочитаю элегантность 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
});

для выполнения действий, которые мне нужно защитить в главном потоке, не беспокоясь о том, в каком потоке был выполнен оригинальный метод.

22 голосов
/ 07 марта 2011

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.Когда таймер срабатывает, поток пытается удалить сообщение из цикла выполнения и выполнить селектор.Это успешно, если цикл выполнения работает и в одном из указанных режимов;в противном случае таймер ожидает, пока цикл выполнения не перейдет в один из этих режимов.

1 голос
/ 08 марта 2011

Путь GCD предполагается более эффективным и простым в обращении, и он доступен только в iOS4 и более поздних версиях, тогда как executeSelector поддерживается в более старых и новых версиях iOS.

...