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.

Другие вопросы по тегам