Проблемы, связанные с вызовом методов UIKit из неосновного потока
Я реализовал метод входа в систему таким образом:
[KVNProgress show];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//some error handling like:
if ([_usernameField.text length] < 4) {
[KVNProgress showErrorWithStatus:@"Username too short!"];
_passwordField.text = @"";
return;
}
//Then I call login web service synchronously here:
result = [ServerRequests login];
dispatch_async(dispatch_get_main_queue(), ^{
if(!result)
{
[KVNProgress showErrorWithStatus:@"problem!" completion:NULL];
_passwordField.text = @"";
}
else if([result.successful boolValue])
{
[KVNProgress showSuccessWithStatus:result.message];
}
});
});
В основном он разбился, и окружающие блоки только с Главной очередью (без приоритета по умолчанию) решили эту проблему! но проблема в том, что KVNProgress показывает только в области обработки ошибок, а не следующую часть, которую мы называем веб-сервисом. Это совсем не удобно для пользователя! Любая идея приветствуется:)
1 ответ
Вы ДОЛЖНЫ вызывать методы, которые обновляют пользовательский интерфейс любым способом из основного потока, согласно UIKit
документация:
По большей части, используйте классы UIKit только из основного потока вашего приложения. Это особенно актуально для классов, производных от UIResponder или связанных с каким- либо образом манипулированием пользовательским интерфейсом вашего приложения.
Я предлагаю вам попытаться ограничить количество обратных вызовов для основного потока, поэтому вы хотите объединить столько обновлений пользовательского интерфейса, сколько сможете.
Тогда все, что вы должны сделать, как вы правильно сказали, это использовать dispatch_async
для обратного вызова вашего основного потока всякий раз, когда вам нужно обновить пользовательский интерфейс из фоновой обработки.
Поскольку он асинхронный, он не будет прерывать вашу фоновую обработку и должен иметь минимальное прерывание для самого основного потока, поскольку обновление значений в большинстве UIKit
компоненты довольно дешевы, они просто обновят свою стоимость и активируют setNeedsDisplay
так что они будут перерисованы при следующем цикле выполнения.
Из вашего кода похоже, что ваша проблема в том, что вы вызываете это из фонового потока:
if ([_usernameField.text length] < 4) {
[KVNProgress showErrorWithStatus:@"Username too short!"];
_passwordField.text = @"";
return;
}
Это 100% код обновления пользовательского интерфейса, и поэтому он должен выполняться в основном потоке.
Хотя я понятия не имею о безопасности потоков KVNProgress
Я предполагаю, что он также должен вызываться в главном потоке, поскольку он представляет ошибку пользователю.
Следовательно, ваш код должен выглядеть примерно так (при условии, что он начинается с основного потока):
[KVNProgress show];
//some error handling like:
if ([_usernameField.text length] < 4) {
[KVNProgress showErrorWithStatus:@"Username too short!"];
_passwordField.text = @"";
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Then I call login web service synchronously here:
result = [ServerRequests login];
dispatch_async(dispatch_get_main_queue(), ^{
if(!result) {
[KVNProgress showErrorWithStatus:@"problem!" completion:NULL];
_passwordField.text = @"";
} else if([result.successful boolValue]) {
[KVNProgress showSuccessWithStatus:result.message];
}
});
});