简体   繁体   中英

Reload data of UITableView in background

In my app, I have a UITableViewController.

Its tableView is divided in 3 sections.

I download datas for each of those sections from my server. To do this, I have 3 functions (for example f1 f2 and f3). Each updates a corresponding NSArray, used as data source for my table.

Now what I want is to reload datas using this functions and refresh my tableView once this 3 functions are done, but without disturbing the user.

I'm not used with asynchronous request, blocks, threads etc... and I'm looking for tips.

Actually, here is what I do :

-(void)viewDidLoad
{
    //some settings

    [NSTimer scheduledTimerWithTimeInterval:15.0 target:self selector:@selector(reloadDatas) userInfo:nil repeats:YES];

    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        [self reloadDatas];
    });
}

-(void)reloadDatas
{
    dispatch_queue_t concurrentQueue = dispatch_get_main_queue();
    dispatch_async(concurrentQueue, ^{
        [self f1];
        [self f2];
        [self f3];
        [myDisplayedTable reloadData];
    });
}

-(void)f1
{
    //load datas with a url request and update array1
}
-(void)f2
{
    //load datas with a url request and update array2
}
-(void)f3
{
    //load datas with a url request and update array3
}

But here, my tableView is "frozen" until it is refreshed.

I don't care about the order of execution of f1 f2 and f3, but I need to wait for this 3 functions to be done before refresh my tableView.

Thanks for your help.

EDIT

Thanks for all your answers.

Here is the working solution :

As mros suggets, I removed the dispatch queue from the viewDidLoad, and replace in reloadDatas:

dispatch_queue_t concurrentQueue = dispatch_get_main_queue();

with

dispatch_queue_t mainThreadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

And finally, I reload my table in a main thread

dispatch_async(dispatch_get_main_queue(), ^{ [myDisplayedTable reloadData]; });

So your "background thread" is actually your main thread. You have to use dispatch_get_global_queue and specify a priority to actually get a different thread. Also, the dispatch async in viewDidLoad is useless as all view controller lifecycle methods are called in the main thread. I would recommend doing something as follows in your f1, f2 and f3 methods:

Start by launching an asynchronous url request, then in the completion block, update arrayX and reload a particular section of your tableview. This way all three requests can happen simultaneously and the table just updates the necessary data when each one finishes. Alternatively, if you only want to reload once, just replace the concurrentQueue variable you have with a background thread and then perform [tableView reloadData] on the main thread.

The previous answers are absolutely right. However your implementation of reloadDatas & viewDidLoad is a bit problematic.

Just to clarify:

You want to complete the time consuming data loading stuff in a background thread, then update the UI/Cells when your data is ready on the main thread.

Like so:

  -(void)viewDidLoad
    {
       dispatch_queue_t concurrentQueue = dispatch_queue_create("com.my.backgroundQueue", NULL);
        dispatch_async(concurrentQueue, ^{
            [self reloadDatas];
        });
    }

-(void)reloadDatas
{
    // Expensive operations i.e pull data from server and add it to NSArray or NSDictionary
      [self f1];
      [self f2];
      [self f3];

    // Operation done - now let's update our table cells on the main thread  

    dispatch_queue_t mainThreadQueue = dispatch_get_main_queue();
    dispatch_async(mainThreadQueue, ^{

        [myDisplayedTable reloadData]; // Update table UI
    });
}

In reloadDatas method you should change this line:

dispatch_queue_t concurrentQueue = dispatch_get_main_queue();

To:

dispatch_queue_t concurrentQueue = dispatch_queue_create("some queue", NULL);

But when you call [myDisplayedTable reloadData] , you need to call this operation in the main queue.

dispatch_async(dispatch_get_main_queue(), ^{ [myDisplayedTable reloadData]; });

One other thing. Pulling data from a server and updating table cells is pretty common. No need for queues or timers here. Here's an alternative structure.

Say you're pulling mp3's from your server : Your model class is : Music.h/m Your Model manager is : MusicManager.h/m (Singleton) - it will contain an array of music objects - that singleton is basically your datasource; and finally your UItableViewController : MusicTableVC.h/m

In MusicManager.h/m : You have an NSMutableArray which will be loaded with Music.h objects that you've pull from the server. You can do that as soon as you app loads without even waiting for the TableViewController.

Inside MusicManager you have a few helper methods to add or remove items from the mutableArray and provide the count and of course your networking methods.

Finally : Post a notification in your network code. Your UITableViewController should listen/observe that notification and "reload" accordingly.

 [[NSNotificationCenter defaultCenter] postNotificationName:@"NewMusicAdded" object:nil];

You query data from your server, parse the data into Music objects add them to your NSMutable array and post a notification to let the table update itself.

Pretty standard recipe.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM