Выполнять изменения пользовательского интерфейса в основной теме с помощью 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
.