从非主线程调用 UIKit 方法的相关问题
Issues related to calling UIKit methods from non-main thread
我是这样实现登录方式的:
[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];
}
});
});
它崩溃了大部分,并且被只有主队列(没有优先级默认值)的周围块解决了!但问题 is:KVNProgress 只出现在错误处理区域,而不是我们称为 web 服务的下一部分。它根本不是用户友好的!欢迎任何想法:)
您必须根据the UIKit
documentation从主线程调用以任何方式更新用户界面的方法:
For the most part, use UIKit classes only from your app’s main thread. This is particularly true for classes derived from UIResponder or that involve manipulating your app’s user interface in any way.
我建议您尽量限制对主线程的回调次数,因此您希望尽可能多地将用户界面更新批处理在一起。
然后你所要做的,正如你正确地说的那样,就是在你需要更新 UI 时使用 dispatch_async
回调你的主线程,从在您的后台处理中。
因为它是异步的,所以它不会中断您的后台处理,并且应该对主线程本身造成最小的中断,因为在大多数 UIKit
组件上更新值相当便宜,它们只会更新它们的值值并触发它们的 setNeedsDisplay
以便它们将在下一个 运行 循环中重新绘制。
从你的代码来看,你的问题似乎是你从后台线程调用它:
if ([_usernameField.text length] < 4) {
[KVNProgress showErrorWithStatus:@"Username too short!"];
_passwordField.text = @"";
return;
}
这是 100% UI 更新代码,因此应该在主线程上进行。
尽管我不知道 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];
}
});
});
我是这样实现登录方式的:
[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];
}
});
});
它崩溃了大部分,并且被只有主队列(没有优先级默认值)的周围块解决了!但问题 is:KVNProgress 只出现在错误处理区域,而不是我们称为 web 服务的下一部分。它根本不是用户友好的!欢迎任何想法:)
您必须根据the UIKit
documentation从主线程调用以任何方式更新用户界面的方法:
For the most part, use UIKit classes only from your app’s main thread. This is particularly true for classes derived from UIResponder or that involve manipulating your app’s user interface in any way.
我建议您尽量限制对主线程的回调次数,因此您希望尽可能多地将用户界面更新批处理在一起。
然后你所要做的,正如你正确地说的那样,就是在你需要更新 UI 时使用 dispatch_async
回调你的主线程,从在您的后台处理中。
因为它是异步的,所以它不会中断您的后台处理,并且应该对主线程本身造成最小的中断,因为在大多数 UIKit
组件上更新值相当便宜,它们只会更新它们的值值并触发它们的 setNeedsDisplay
以便它们将在下一个 运行 循环中重新绘制。
从你的代码来看,你的问题似乎是你从后台线程调用它:
if ([_usernameField.text length] < 4) {
[KVNProgress showErrorWithStatus:@"Username too short!"];
_passwordField.text = @"";
return;
}
这是 100% UI 更新代码,因此应该在主线程上进行。
尽管我不知道 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];
}
});
});