如何通知UITableViewController数据已准备好显示?

时间:2011-06-24 10:25:09

标签: iphone objective-c asynchronous uitableview grand-central-dispatch

我有一个UITableViewController,我想在相应模型的数据准备好显示后通知我。问题是这些数据是从Web服务获取的,请求可能需要几秒钟。目前,我正在主线程上同步获取数据,这当然导致我的主线程被阻塞。现在,我不希望我的控制器知道有关从互联网下载数据的任何信息。我怎样才能做到这一点。目前,我正在考虑使用GCD并实现类似-loadDataWithCallback:的方法,并提供一个成功触发[tableView reloadData]的回调。这是一个好方法吗?是否还有其他可能通知控制器模型准备好了?我的另一个想法是使用委托机制并将控制器设置为我的模型的委托?

总结一下,有什么更好的:带回调的GCD或实现自己的委托机制?

还有其他可能吗?


更新:2011年6月24日13:15 CET

在阅读完所有回复后,我得出的结论是,我的问题有三种可能的解决方案:

  1. 利用NSNotifications并使用NSURLConnection来实现异步。下载

  2. 实施自定义协议并使用委派机制。再次,使用NSURLConnection来实现异步。下载。

  3. 在单独的GCD队列中使用同步下载并使用回调。

  4. 由于没有人赞成最后的解决方案,我想深入讨论这种方法。在看到通知处理中涉及的所有代码之后,我认为GCD是一种更好的方法。而不是同意某个必须以某种方式记录的通知,以便每个开发人员都知道它,我可以简单地使用回调。一方面,它给了我一个清晰的界面,就像我在使用代表时所拥有的那样,另一方面,它给了我完全的灵活性。你真的认为GCD要复杂吗?这是我的代码:

    - (void)loadRestaurantsWithCallback:(void (^)())callback
    {
        dispatch_queue_t current_queue = dispatch_get_current_queue();
        dispatch_queue_t download_queue = dispatch_queue_create("Download queue", NULL);
    
        dispatch_async(download_queue, ^{
            self.restaurants = [self loadRestaurants];
            dispatch_async(current_queue, ^{ callback(); });
        });
    
        dispatch_release(download_queue);
    }
    

    顺便说一下,我的应用程序只显示我大学不同食堂的菜单。

    在我的控制器中,我只是执行以下操作:

    if (![self.canteen hasRestaurants]) {
        [self.canteen loadRestaurantsWithCallback:^{
            [self.tableView reloadData];
        }];
    }
    

    它就像一个魅力。您对此解决方案有何看法?


    更新:2011年6月24日16:30 CET

    这个问题有第四个解决方案,即使它涉及的代码多于GCD方法,也可能是它的方法。以下是我提出的建议:

    1. 使用NSURLConnection进行异步下载。

    2. 让您的模型响应NSURLConnection实例发送的回调。

    3. 使用键值编码键值观察

    4. 在您的控制器中,您只需执行以下操作:

      [self.model addObserver:self forKeyPath:@"method-name" options:0 context:NULL];
      

      - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
      {
          [self.tableView reloadData];
      }
      

5 个答案:

答案 0 :(得分:4)

使用NSNotificationCenter并创建本地通知类型并在获取数据后发布的最佳方式。

首先注册通知类型 DataUpdateNotification

- (void)viewWillAppear:(BOOL)animated 
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(receiveDataNotification:) 
                                                 name:@"DataUpdateNotification"
                                               object:nil];
   ...............

}

实施receiveDataNotification:处理DataUpdateNotification类型通知。

- (void) receiveDataNotification:(NSNotification *) notification
{
     if ([[notification name] isEqualToString:@"DataUpdateNotification"])
     {
        NSLog (@"Successfully received the Data Update notification!");
     }
}

当控制器消失时,从对象实例中删除通知。

- (void)viewWillDisappear:(BOOL)animated 
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];

}

现在发布应用程序任何部分的通知..

- (void) DataUpdated
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"DataUpdateNotification" object:self];
}

答案 1 :(得分:1)

我的建议是使用异步通信而不是多线程(GCD)。它更容易处理,它可以满足您的需求。

这个想法是:

  1. 执行Web请求异步,因此您不会阻止;

  2. 异步发出请求时,用它注册“回调”;

  3. 当数据存在时,底层通信基础设施会调用“回调”,以便您知道数据存在;

  4. 回调通常是某些类的委托(即使是控制器也会这样做),其主要职责是更新模型,并重新加载表视图。

  5. 这是一个非常简单和干净的范例。

    因此,您可以调查NSURLConnection/NSURLRequest提供的异步可能性,或者(强烈鼓励)查看ASIHTTRequest,这样可以让您更轻松。

答案 2 :(得分:1)

如何为您的处理类实现委托并使用委托方法通知调用者。

它非常简单易用,在异步场景中完美运行。

定义委托协议并为该类创建一个对象。在调用对象类中实现委托方法,并在操作完成时调用委托。

如果您需要,我很乐意在这里写一些代码。

答案 3 :(得分:1)

如果您正在使用线程..您可以使用

[self performSelectorOnMainThread: @selector ( // ) waitUntilDone:YES]

这将确保您的流程等待直到另一个流程完成。

答案 4 :(得分:0)

我有类似的情况,我使用NSFetchedResultsController来解决它。该表使用获取的结果控制器进行更新,并且客户端以异步方式运行。收到数据后,我创建一个模型并使用CoreData编写它,此时它被提取的结果控制器自动检测到插入,更新或删除到表中。