我有一个UITableViewController
,我想在相应模型的数据准备好显示后通知我。问题是这些数据是从Web服务获取的,请求可能需要几秒钟。目前,我正在主线程上同步获取数据,这当然导致我的主线程被阻塞。现在,我不希望我的控制器知道有关从互联网下载数据的任何信息。我怎样才能做到这一点。目前,我正在考虑使用GCD并实现类似-loadDataWithCallback:
的方法,并提供一个成功触发[tableView reloadData]
的回调。这是一个好方法吗?是否还有其他可能通知控制器模型准备好了?我的另一个想法是使用委托机制并将控制器设置为我的模型的委托?
总结一下,有什么更好的:带回调的GCD或实现自己的委托机制?
还有其他可能吗?
更新:2011年6月24日13:15 CET
在阅读完所有回复后,我得出的结论是,我的问题有三种可能的解决方案:
利用NSNotifications
并使用NSURLConnection
来实现异步。下载
实施自定义协议并使用委派机制。再次,使用NSURLConnection
来实现异步。下载。
在单独的GCD队列中使用同步下载并使用回调。
由于没有人赞成最后的解决方案,我想深入讨论这种方法。在看到通知处理中涉及的所有代码之后,我认为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方法,也可能是它的方法。以下是我提出的建议:
使用NSURLConnection
进行异步下载。
让您的模型响应NSURLConnection
实例发送的回调。
使用键值编码和键值观察。
在您的控制器中,您只需执行以下操作:
[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];
}
答案 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)。它更容易处理,它可以满足您的需求。
这个想法是:
执行Web请求异步,因此您不会阻止;
异步发出请求时,用它注册“回调”;
当数据存在时,底层通信基础设施会调用“回调”,以便您知道数据存在;
回调通常是某些类的委托(即使是控制器也会这样做),其主要职责是更新模型,并重新加载表视图。
这是一个非常简单和干净的范例。
因此,您可以调查NSURLConnection/NSURLRequest
提供的异步可能性,或者(强烈鼓励)查看ASIHTTRequest,这样可以让您更轻松。
答案 2 :(得分:1)
如何为您的处理类实现委托并使用委托方法通知调用者。
它非常简单易用,在异步场景中完美运行。
定义委托协议并为该类创建一个对象。在调用对象类中实现委托方法,并在操作完成时调用委托。
如果您需要,我很乐意在这里写一些代码。
答案 3 :(得分:1)
如果您正在使用线程..您可以使用
[self performSelectorOnMainThread: @selector ( // ) waitUntilDone:YES]
这将确保您的流程等待直到另一个流程完成。
答案 4 :(得分:0)
我有类似的情况,我使用NSFetchedResultsController
来解决它。该表使用获取的结果控制器进行更新,并且客户端以异步方式运行。收到数据后,我创建一个模型并使用CoreData编写它,此时它被提取的结果控制器自动检测到插入,更新或删除到表中。