Objective c UITableView刷新数据

Objective c UITableView刷新数据,objective-c,ios,multithreading,performance,uitableview,Objective C,Ios,Multithreading,Performance,Uitableview,我有一个UITableViewController,打开时显示以下对象的表: class { NSString *stringVal; int value; } 但是,每当这个控制器打开时,我希望它从internet下载数据并显示连接。。。并刷新所有对象的stringVal和值。我通过刷新UITableViewController中的数组来实现这一点。但是,要执行此操作,UI有时会挂起,甚至会显示空白表格单元格,直到操作结束。我在NSOperationQueue中下载数据,但我想知道是

我有一个UITableViewController,打开时显示以下对象的表:

class {
  NSString *stringVal;
  int value;
}
但是,每当这个控制器打开时,我希望它从internet下载数据并显示连接。。。并刷新所有对象的stringVal和值。我通过刷新UITableViewController中的数组来实现这一点。但是,要执行此操作,UI有时会挂起,甚至会显示空白表格单元格,直到操作结束。我在NSOperationQueue中下载数据,但我想知道是否有更好的方法来刷新数据而不出现那些奇怪的UI错误

编辑: 用户界面不再显示空白单元格。这是因为CellForRowatinexPath为我的cellText设置了零值。然而,即使我使用的是NSOperationQueue,调用tableView.reloadData时仍然显得有些滞后

编辑2: 此外,我还有两个问题:1。滚动可防止用户界面被更新和2。当滚动停止,UI开始更新时,它会挂起一点。当您查看包含未读计数的文件夹列表时,可以在本机邮件应用程序中找到我正在尝试执行的操作的完美示例。如果您不断滚动tableview,则文件夹未读计数将在不挂起的情况下更新。

根据您在问题注释中的回答,听起来您好像是从后台线程调用[tableview reloadData]

不要这样做。除非另有规定,否则始终需要从主线程调用UIKit方法。如果不这样做,可能会导致无穷无尽的问题,而你可能正在看到其中的一个

编辑:我误读了你的评论。听起来您不是在从后台线程更新UI。但是我对体系结构的评论,即为什么在下载完成后在后台线程中更新

您声明当数据从服务器返回时,我调用后台操作。。。这听起来有点倒退。通常情况下,您将在后台线程上运行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是个错误。不一定是这样,但处理这一问题超出了问题的范围。

您是在主线程上刷新还是在后台操作中刷新?当数据从服务器返回时,我调用后台操作,将tableView数组设置为新数据,然后在主线程上调用tableView.reloadData。确定,我想我解决了空白单元格的问题。我的数组为空,因此cellForIndexPath返回一些nil值。但是,即使我使用的是NSOperationQueue,当它刷新时,它似乎仍然会稍微挂起。由于某种原因,当我不断上下滚动表时,UI不会刷新。你知道为什么吗?当你说UI没有刷新时,你能解释一下你期望看到什么以及什么没有发生吗?数据是否没有显示在单元格中?好的,我认为这更有意义,但我可能会误导。当数据返回时,我需要在重新加载tableview之前对一些数组进行一些操作。操作完成后,我调用[self.tableView performSelectorOnMainThread:@selectorreloaddatawithobject:nil waitUntilDone:Y
(只有英文),;重新加载数据。数据操纵就是我用NSOperationQueue调用的。问题是当我滚动时,UI在我停止滚动后立即挂起,可能是为了在主线程的tableView上调用reloadData。操纵myURLRequest.dataItems 2。self.tableItems=myURLRequest.dataItems 3。[self-performSelectorOnMainThread:@selectorupdateUI with-Object:nil waitUntilDone:YES];我想我解决了这个问题。。。它挂起是因为当响应从NSURLConnection请求返回时,响应的数据操作和解析正在UI线程上进行。我应该如何处理这种数据操作?NSOperationQueue?那么您正在设置NSURLConnection以使用委托回调?如果是,请查看-setDelegateQueue:。我认为这可能更有意义,例如,将对+NSData*sendSynchronousRequest:NSURLRequest*RequestReturnInResponse:NSURLResponse**response error:NSError**error的调用包装在单独的调度队列上的调度异步中,然后使用另一个调度异步调用主线程。GCD FTW!我认为这个答案是正确的,但我还有几个后续问题。我可以通过使用NSOperationQueue来提高速度,但是,如果我在队列结束之前选择一个单元格并按下一个视图控制器,现在我看到一些奇怪的行为。我认为sendSynchronousRequest没有多大意义。