【问题标题】:UIView does not get updated correctly after setting UIImage and UITextField设置 UIImage 和 UITextField 后 UIView 没有正确更新
【发布时间】:2015-03-22 10:57:22
【问题描述】:

我有一些从服务器获取的数据,然后显示在我的 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 更新之前记录的消息会被打印出来。

感谢您对此的任何帮助。

【问题讨论】:

  • 编辑您的代码以添加您在 UI 中设置值的方式
  • 请告诉我们您设置这些值的代码
  • 谢谢大家。问题就像 JOJO,abdullah an rob 在下面指出的那样。

标签: ios objective-c xcode uiview uiviewcontroller


【解决方案1】:

更新 UI 时确保您在主线程上

【讨论】:

    【解决方案2】:

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

    - (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 代码运行。这会使应用在用户看来像是被冻结了一样。

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

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

    【讨论】:

    • 你这个摇滚人!!非常感谢这个和详细的解释。
    【解决方案3】:

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

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

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

    【讨论】:

    • 不客气。请接受为您提供正确信息以解决问题的答案。我认为@JoJo 的答案是最好的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-24
    • 1970-01-01
    • 2019-02-28
    • 2019-02-27
    • 1970-01-01
    • 1970-01-01
    • 2017-07-06
    相关资源
    最近更新 更多