我有一个UITableViewController
,当打开时会显示以下对象的表格:
class {
NSString *stringVal;
int value;
}
但是,无论何时打开此控制器,我都希望它从Internet下载数据并在状态栏中显示“正在连接...”并刷新stringVal和所有对象的值。我通过刷新UITableViewController
中的数组来完成此操作。但是,要执行此操作,UI有时会挂起,甚至会显示“空白”表格单元格,直到操作结束。我在NSOperationQueue
中执行此操作来下载数据,但我想知道是否有更好的方法来刷新数据而没有那些奇怪的UI错误。
UI不再显示空白单元格。这是因为cellForRowAtIndexPath为我的cellText设置了nil值。但是,即使我使用的是NSOperationQueue,当调用tableView.reloadData时,它看起来仍然有些滞后。
此外,我有两个问题:1。滚动阻止UI更新; 2.当滚动停止并且UI开始更新时,它会挂起一点点。当您查看具有未读计数的文件夹列表时,可以在本机Mail应用程序中找到我正在尝试执行的操作的完美示例。如果您不断滚动表格视图,文件夹未读计数将更新,而不会有任何挂起。
答案 0 :(得分:0)
根据您在问题评论中的回答,听起来好像是从后台线程调用[tableView reloadData]。
不要这样做。 UIKit方法,除非另有说明,否则需要从主线程调用始终。如果不这样做就不会导致问题的结束,你可能会看到其中一个。
您声明“当数据从服务器返回时,我会调用后台操作......”这听起来倒退了。通常你会在后台线程上运行你的NSURLConnection(或者你用来下载的任何东西),以免阻塞到UI,然后调用主线程来更新数据模型并刷新UI。或者,使用异步NSURLConnection(管理自己的后台线程/队列),例如:
[NSURLConnection sendAsynchronousRequest:(NSURLRequest *)
requestqueue:(NSOperationQueue *)queue
completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler];
并确保为队列使用[NSOperationQueue mainQueue]。
您还可以使用GCD,即嵌套的dispatch_async()调用(后台队列外部用于处理同步连接,主队列内部用于处理连接响应)。
最后,我会注意到您原则上可以在后台线程上更新您的数据模型,只需从主线程刷新UI。但这意味着您需要注意使您的模型代码线程安全,您可能至少会搞乱几次。由于更新模型可能不是一个耗时的步骤,我也会在主线程上执行此操作。
编辑:
我正在添加一个如何使用GCD和同步请求来完成此任务的示例。显然,有很多方法可以完成非阻塞URL请求,我并不断言这是最好的方法。在我看来,它确实具有在一个地方保留处理请求的所有代码的优点,使其更容易阅读。
代码有很多粗糙的边缘。例如,通常不需要创建自定义调度队列。它盲目地假定返回的网页的UTF-8编码。并且没有任何内容(保存HTTP错误描述)已本地化。但它确实演示了如何运行非阻塞请求和检测错误(在网络和HTTP层)。希望这有用。
NSURL *url = [NSURL URLWithString:@"http://www.google.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
dispatch_queue_t netQueue = dispatch_queue_create("com.mycompany.netqueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(netQueue,
^{
// We are on a background thread, so we won't block UI events (or, generally, the main run loop)
NSHTTPURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
dispatch_async(dispatch_get_main_queue(),
^{
// We are now back on the main thread
UIAlertView *alertView = [[UIAlertView alloc] init];
[alertView addButtonWithTitle:@"OK"];
if (data) {
if ([response statusCode] == 200) {
NSMutableString *body = [[NSMutableString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
[alertView setTitle:@"Success"];
[alertView setMessage:body];
}
else {
[alertView setTitle:@"HTTP Error"];
NSString *status = [NSHTTPURLResponse localizedStringForStatusCode:[response statusCode]];
[alertView setMessage:status];
}
}
else {
[alertView setTitle:@"Error"];
[alertView setMessage:@"Unable to load URL"];
}
[alertView show];
[alertView release];
});
});
dispatch_release(netQueue);
编辑:
哦,还有一个更大的粗糙边缘。上面的代码假定任何HTTP状态代码!= 200都是错误的。情况不一定如此,但处理此问题超出了本问题的范围。