Reputation: 2623
I have a UITableViewController
that I'd like to notify once the data of the corresponding model is ready to be displayed. The problem is that this data is fetched from a web service and the request can take up to several seconds. Currently, I'm fetching the data synchronously on the main thread which, of course, causes my main thread to block. Now, I don't want my controller to know anything about downloading data from the internet. How can I accomplish this. Currently, I'm thinking about utilizing GCD and implementing a method like -loadDataWithCallback:
and provide a callback that triggers a [tableView reloadData]
on success. Is this a good approach? Are there other possibilities to notify a controller that the model is ready? An other idea I had was to use a delegate mechanism and setting the controller as a delegate of my model?
To summarize, what's better: GCD with callbacks or implementing your own delegate mechanism?
Are there other possibilities?
Update: June, 24th 2011 13:15 CET
After reading all your replies, I come to the conclusion that there are 3 possible solutions to my problem:
Make use of NSNotifications
and use NSURLConnection
to implement async. download
Implement a custom protocol and use a delegation mechanism. Again, use NSURLConnection
to implement async. download.
Use synchronous download in a separate GCD queue and use callbacks.
Since nobody favors the last solution, I want to discuss this approach a little in depth. After seeing all the code that is involved in notification handling, I think that GCD is a better approach. Instead of agreeing on a certain notification which has to be somehow documented, so that every developer knows about it, I can simply use a callback. On the one hand, it gives me a clear interface like one I would have when I would use a delegate, on the other hand, it give me total flexibility. Do you really think that GCD is to complicated for this? Here is my code:
- (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);
}
Btw., my application simply displays the menus of the different canteens at my university.
In my controller, I simply do the following:
if (![self.canteen hasRestaurants]) {
[self.canteen loadRestaurantsWithCallback:^{
[self.tableView reloadData];
}];
}
It works like a charm. What do you think about this solution?
Update: June, 24th 2011 16:30 CET
There is a fourth solution to this problem and it's probably the way to go even if it involves more code than the GCD approach. Here is what I came up with:
Use NSURLConnection
to do asynchronous downloading.
Have your model respond to the callbacks sent by the NSURLConnection
instance.
Use Key-Value Coding and Key-Value Observing.
In your controller, you simply have to do the following:
[self.model addObserver:self forKeyPath:@"method-name" options:0 context:NULL];
and
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
[self.tableView reloadData];
}
Upvotes: 5
Views: 883
Reputation: 18333
I have a similar situation and I use NSFetchedResultsController
to solve it. The table is updated with the fetched results controller, and a client runs asynchronously. When data is received I create a model and write it using CoreData, at which time it is automatically detected by the fetched results controller for an insert, update, or delete into the table.
Upvotes: 0
Reputation: 12325
if you are using threads.. you can use
[self performSelectorOnMainThread: @selector ( // ) waitUntilDone:YES]
This will make sure that your process waits until another process gets Done.
Upvotes: 1
Reputation: 10393
How about implementing a delegate for your processing class and using the delegate method to inform the caller.
Its pretty simple and easy to get hold off and works perfectly in such async scenarios.
Define delegate protocol and create a object for the class. Implement the delegate methods in the calling object class and when action is complete call the delegate.
I'd be happy to write some code here if you need.
Upvotes: 1
Reputation: 31722
The best way to use the NSNotificationCenter
and create a local notification type and post it once you get the data.
First register for the notification type DataUpdateNotification .
- (void)viewWillAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(receiveDataNotification:)
name:@"DataUpdateNotification"
object:nil];
...............
}
Implement receiveDataNotification: to handle DataUpdateNotification
type notification.
- (void) receiveDataNotification:(NSNotification *) notification
{
if ([[notification name] isEqualToString:@"DataUpdateNotification"])
{
NSLog (@"Successfully received the Data Update notification!");
}
}
Remove the notification from your object instance when your controller is disappeared.
- (void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Now Post the notification from any part of your application ..
- (void) DataUpdated
{
[[NSNotificationCenter defaultCenter] postNotificationName:@"DataUpdateNotification" object:self];
}
Upvotes: 4
Reputation: 69027
My suggestion is using asynchronous communication instead of multi threading (GCD). It is much easier to deal with and it does what you need.
The idea is:
you execute the web request async, so you don't block;
when issuing the request asynchronously, you register a "callback" with it;
when the data is there, the underlying communication infrastructure calls the "callback" so that you know data is there;
the callback is typically a delegate of some of your classes (even you controller will do), and its main duty is updating your model, and issuing a reload of the table view.
It is a very simple and clean paradigm to use.
So, you can investigate the async possibilities offered by NSURLConnection/NSURLRequest
, or (strongly encouraged) have a look at ASIHTTRequest, which will make your like much easier.
Upvotes: 1