设置UIImage和UITextField后,UIView无法正确更新

时间:2015-03-22 10:57:22

标签: ios objective-c xcode uiview uiviewcontroller

我从服务器获取了一些数据,然后在我的UIViewController类中显示。为此,我有两节课。 UIViewController和另一个名为ServerCommunicator的。 UIViewController是ServerCommunicator类的委托。 serverCommunicator如下所示:

  - (void)fetchServerData:(NSString *) serverAddress{

    NSURL *url = [[NSURL alloc] initWithString:serverAddress];

    [NSURLConnection sendAsynchronousRequest:[[NSURLRequest alloc] initWithURL:url] queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

        if (error) {
            [self.delegate fetchingSongsFailedWithError:error];
        } else {
            [self.delegate receivedSongsJSON:data];
        }
    }];
}

UIViewController分配serverCommunicator,将自己设置为委托,然后发出获取请求。

 - (void)viewDidLoad {
    [super viewDidLoad];

    self.songServerCommunicator = [[serverCommunicator alloc] init];
    self.songServerCommunicator.delegate = self;
    [self.songServerCommunicator fetchServerData:<some_server_ip>];

}

执行此操作后,它会实现所需的协议方法:

- (void)receivedSongsJSON:(NSData *)data{
         NSLog(@"received server response");
        /* Parses the data and displays in textfield/imageview */
} 

我的问题是,当我显示在委托方法中收到的数据时,它不会立即在UI中反映出来。这很奇怪,有时它会自动显示20秒钟,有时则需要一分钟。我不知道最近发生了什么。我知道数据是立即获取的,因为在UIView更新之前记录的消息会被打印出来。

感谢您提供任何帮助。

3 个答案:

答案 0 :(得分:1)

确保在更新UI时位于主线程上

答案 1 :(得分:0)

receiveSongsJSON()方法是从给予[NSURLConnection sendAsynchronousRequest]的回调中调用的,我认为这是从后台线程调用的。

即使在UIViewController中声明了receiveSongsJSON()方法,如果从一个调用它,它也将在后台线程中执行。

正如@robdashnash所示,请确保从主线程调用所有UI更新代码。如果您不确定如何操作,请查看Grand Central Dispatch(GCD)here的文档。

答案 2 :(得分:0)

其他人已经指出了这个问题,但他们没有提供具体代码的解决方案。这是编码解决方案:

- (void)fetchServerData:(NSString *) serverAddress{

    NSURL *url = [[NSURL alloc] initWithString:serverAddress];

    [NSURLConnection sendAsynchronousRequest:[[NSURLRequest alloc] initWithURL:url] queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        dispatch_async(
            dispatch_get_main_queue(), 
            ^void {
                if (error) {
                    [self.delegate fetchingSongsFailedWithError:error];
                } else {
                    [self.delegate receivedSongsJSON:data];
                }
            }
        ); 
    }];
}

您必须了解Grand Central Dispatch在iOS中的运作方式。 GCD是多线程的抽象层。

运行UI的主队列是一个串行队列。这意味着您无法同时运行两个代码块。如果您要在主队列上执行长网络查询,那将是糟糕的用户体验,因为它会阻止任何UI代码运行。这会使应用看起来像被冻结给用户。

为解决冻结用户界面问题,iOS为您提供了其他队列,可以在不阻塞主队列的情况下完成工作。 iOS提供了创建自己的自定义队列或使用预先制作的全局并发队列的方法。我们不知道NSURLConnection使用的队列,因为我们没有Cocoa源代码。但我们所知道的是NSURLConnection绝对不使用主队列,因为UI在从服务器获取数据时不会被冻结。

由于NSURLConnection运行在与运行主队列(UI)不同的线程上,因此您无法在任何随机时间更新UI。这是多线程程序的常见问题。当多个线程访问相同的代码块时,指令可能会交错,并且两个块都会获得意外的结果。您遇到的意外结果之一是最终更新UI的20秒延迟。要防止交错,您需要在同一个线程上运行所有UI代码。您可以通过将UI更新代码排入主队列的末尾来完成此操作。