Выполнять изменения пользовательского интерфейса в основной теме с помощью dispatch_async или выполнитьSelectorOnMainThread?

Возможный дубликат:
Grand Central Dispatch (GCD) vs. performSelector - нужно лучшее объяснение

Чтобы выполнить "материал" в основном потоке, следует ли использовать dispatch_async или performSelectorOnMainThread? Есть ли предпочтительный способ, правильный/неправильный и/или лучший способ?

Пример: Я выполняю некоторую логику в блоке метода NSURLConnection sendAsynchronousRequest:urlRequest. Поскольку я делаю материал на главном представлении, например, представляя UIAlertView, мне нужно показать UIAlertView в основном потоке. Для этого я использую следующий код.

[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

    // code snipped out to keep this question short

    if(![NSThread isMainThread]) 
    {
        dispatch_async(dispatch_get_main_queue(), ^{
                    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Oops!" message:@"Some Message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
                    [alertView show];
        });
    }
}];

Внутри этого же оператора if(![NSThread isMainThread]) я также вызываю некоторые пользовательские методы. Вопрос в том, должен ли я использовать метод dispatch_async, который я использую выше, или лучше использовать performSelectorOnMainThread вместо этого? Например, полный код ниже:

[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

    // code snipped out to keep this question short

    if(![NSThread isMainThread]) 
    {
        dispatch_async(dispatch_get_main_queue(), ^{
                    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Oops!" message:@"Some Message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
                    [alertView show];

            // call custom methods in dispatch_async?
            [self hideLoginSpinner];
        });

        // or call them here using performSelectorOnMainThread???
        [self performSelectorOnMainThread:@selector(hideLoginSpinner) withObject:nil waitUntilDone:NO];
    }
}];

FYI. Если я НЕ выполняю эти действия в основном потоке, я вижу несколько секунд задержки при представлении UIAlertView, и я получаю следующее сообщение в отладчике wait_fences: failed to receive reply: 10004003. Я узнал, что это потому, что вам нужно внести изменения в пользовательский интерфейс в основном потоке... В случае, если кто-то задается вопросом, почему я делаю то, что я делаю...

Ответы

Ответ 1

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

  • dispatch_async имеет большое преимущество в том, что компилятор выполняет все свои обычные тесты. Если вы ошибаетесь в методе в performSelectorOnMainThread, вы не выполняете время выполнения, а не компилируете время.
  • dispatch_async упрощает возврат данных из основного потока с помощью квалификатора __block.
  • dispatch_async упрощает обработку примитивных аргументов, поскольку вам не нужно их обертывать в объект. Однако это связано с потенциальной ловушкой. Если у вас есть указатель на некоторые данные, помните, что захват блока не выполняет глубокую копию данных. С другой стороны, обертывание данных в объект, как вы должны были бы сделать для performSelectorOnMainThread, делает глубокую копию (если вы не установите специальные параметры). Без глубокой копии вы можете столкнуться с прерывистыми ошибками, которые разочаровывают отладки. Это означает, что перед тем, как вы вызовете dispatch_async, вы должны обернуть вещи, как char * в NSString.