UITableView reloadData is slow - ios

I have a function that connects to the internet and then refreshes the cells in the table.
My function is:
- (void) updateMethod
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = #"Data request queue";
[queue addOperationWithBlock:^{
//neither of these delay the responsiveness of the table
[columnArrayBackground removeAllObjects];
[self getColumnDataBackground];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
for (int i=0; i < 6; i++) {
columnArray[i] = columnArrayBackground[i];
};
//THIS ONE LINE CAUSES DELAYS
[homeTable reloadData];
}];
}];
}
Everything is super speedy EXCEPT for [homeTable reloadData]. When I comment that out, I have quick response. When I uncomment it, my cell response lags sometimes by a few seconds!
My other reloadData calls do not delay my app. Am I not implementing NSOperationQueue correctly?

Remove the queues from your update method and leave only what updates the table. Under the action that refreshes your table add this code. Where the updateMethod is the code that updates the table. Only when you are back on the main thread do you reload the data. Hope this helps!
//perform on new thread to avoid the UI from freezing
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
^{
//background thread code
[self performSelector:#selector(updatemethod:) withObject:nil];
dispatch_async(dispatch_get_main_queue(),
^{ //back on main thread
[self.tableView reloadData];
});});

Related

Cancel or destroy a looping background thread on viewcontroller close

I am trying to implement an auto save function to my app and having troubles with killing my background loop when the viewcontroller is no longer active.
This is currently what my method looks like:
-(void)saveTimer{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:15];
NSLog(#"saving now");
[self save:self];
[self saveTimer];
});
}
I have read a little that I may not be able to cancel a global thread this this so I have also looked at using NSOperationQueue like this:
myQueue = [[NSOperationQueue alloc] init];
[myQueue addOperationWithBlock:^{
// Background work
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// Main thread work (UI usually)
}];
}];
But dont know how to cancel or destroy this either.
I'd recommend using an NSTimer instead. This way you could fire your timer like this (holding a reference to it):
timer = [NSTimer scheduledTimerWithTimeInterval:15 target:self selector:#selector(save:self:) userInfo:nil repeats:YES];
And then, when you want to stop it, just call
[timer invalidate];
timer = nil;
You can cancel your operationQueue in -dealloc() method of your viewController. As such:
(void)dealloc {
[_queue cancelAllOperations];
[_queue release];
}

iOS7: Multi-UITableview rendering lag

I have a viewcontroller in a universal app which has 5 coloumns (UITableview). Initially I fetch data from CoreData and then categorise it in 5 NSArrays. After that I call all 5 UITableViews to reload via following code.
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[_tvLeftMost reloadData];
}];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[_tvLeft reloadData];
}];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[_tvCenter reloadData];
}];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[_tvRight reloadData];
}];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[_tvRightMost reloadData];
}];
on both IOS7&8(iPAD3) the data is fetched in 0.5 secs. But reload tables takes 5 secs on iOS8 and 20+ secs on iOS7. The UITableView's cell are not complex and only involve a local UIImage & a UILabel. How can I decrease rendering time on iOS7?
Try to reload your UITableView in below way -
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
If your UITableView load image from web url , then it must be async image loading. Please check this tutorial for async image loading.

Wait for an async methods to finish in a for loop

I have a for loop containing three asynchronous methods, and I want to make some treatment after this 3 async methods are finished.
-(void)getAllUsersInformations{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for(User *user in users){
[self getUserInfo:user];
}
//Here, I want to reload the table view for example, after finishing the for loop (executing the whole three methods).
});
}
-(void)getUserInfo:(User*)user{
[self getInformations:user];
[self getExperiences:user];
[self getEducation:user];
}
Do you have any technic to have this result?
Thank you very much.
One GCD approach is to use dispatch_group. So, before you start an asynchronous task, call dispatch_group_enter, and then when the asynchronous task finishes, call dispatch_group_leave, and you can then create a dispatch_group_notify which will be called when the asynchronous tasks finish. You can marry this with a completion block pattern (which is a good idea for asynchronous methods, anyway):
If getInformations, getExperiences and getEducation are, themselves, all asynchronous methods, the first thing you need is some mechanism to know when they're done. A common solution is to implement a completion block pattern for each. For example:
// added completionHandler parameter which will be called when the retrieval
// of the "informations" is done.
- (void)getInformations:(User*)user completionHandler:(void (^)(void))completionHandler {
// do whatever you were before, but in the asynchronous task's completion block, call this
// completionHandler()
//
// for example
NSURLRequest *request;
[NSURLConnection sendAsynchronousRequest:request queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// handle the request here
// the important thing is that the completion handler should
// be called _inside_ the this block
if (completionHandler) {
completionHandler();
}
}];
}
Repeat this process for getExperiences and getEducation, too.
Then, you can use a dispatch group to notify you of when each of these three requests are done done, calling a completion block in getUserInfo when that takes place:
// added completion handler that will be called only when `getInformations`,
// `getExperiences` and `getEducation` are all done.
//
// this takes advantage of the completion block we added to those three
// methods above
- (void)getUserInfo:(User*)user completionHandler:(void (^)(void))completionHandler {
dispatch_group_t group = dispatch_group_create();
// start the three requests
dispatch_group_enter(group);
[self getInformations:user completionHandler:^{
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self getExperiences:user completionHandler:^{
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self getEducation:user completionHandler:^{
dispatch_group_leave(group);
}];
// this block will be called asynchronously only when the above three are done
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
if (completionHandler) {
completionHandler();
}
});
}
And you then repeat this process at the getAllUsersInformations:
// call new getUserInfo, using dispatch group to keep track of whether
// all the requests are done
-(void)getAllUsersInformations {
dispatch_group_t group = dispatch_group_create();
for(User *user in users){
dispatch_group_enter(group);
[self getUserInfo:user completionHandler:^{
dispatch_group_leave(group);
}];
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
Two final thoughts:
Having outlined all of that, I must confess that I would probably wrap these requests in concurrent/asynchronous custom NSOperation subclasses instead of using dispatch groups. See the "Configuring Operations for Concurrent Execution" section of the Concurrency Programming Guide. This is a more radical refactoring of the code, so I won't tackle that here, but it lets you constrain the number of these requests that will run concurrently, mitigating potential timeout issues.
I don't know how many of these user requests are going on, but you might want to consider updating the UI as user information comes in, rather than waiting for everything to finish. This is, again, a more radical refactoring of the code, but might lead to something that feels more responsive.
Try to do a block with completion, you can't do this with a for loop if the methods are async. you have to call getUserInfo one by one after the completion of the previous. I think this gonna be solved your problem.
-(void)getAllUsersInformations{
[self registerUserAtIndex:0];
}
- (void) registerUserAtIndex: (NSInteger ) userIndex
{
RegisterOperation *op = [[RegisterOperation alloc] initWithUser:[users objectAtIndex:userIndex]];
[RegisterOperation setResultCompletionBlock:^(BOOL *finished, NSInteger userIndex) {
dispatch_async(dispatch_get_main_queue(), ^{
if (userIndex++ < [users count] {
[self registerUserAtIndex:userIndex++];
} else {
[myTableView reloadData];
}
}];
[[NSOperationQueue mainQueue] addOperation:op];
}
Hope this will help you.
Rop Answer with swift:
func processData()
{
let group: dispatch_group_t = dispatch_group_create()
for item in data as! Object {
dispatch_group_enter(group)
item.process(completion: {() -> (Void) in
dispatch_group_leave(group)
})
}
dispatch_group_notify(group, dispatch_get_main_queue(), {
//Do whatever you want
})
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
for(User *user in users){
[self getUserInfo:user];
}
dispatch_async(dispatch_get_main_queue(), ^{
//reload tableview , this is on main thread.
});
});

How to Call text posting method after image posting method get done in IOS

I used below code for calling image posting first and then text posting upon completion of image posting method...
-(IBAction)btnChooseClecked:(id)sender
{
NSOperationQueue *queue = [NSOperationQueue new];
queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock:^{
[self sendImage];
}];
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:1.0];//2.0
[self sendText];
}];
///(OR) i used below code also
dispatch_async(dispatch_get_main_queue(), ^{
[self sendImage];
// inside sendImage method I am calling sendText as [self performSelector:#selector(sendText) withObject:nil afterDelay:0.2];
})
}
-(void)sendImage
{
NSData *imageData = UIImageJPEGRepresentation([appDelegate scaleAndRotateImage:imageSelected.image], 0.0);
[appDelegate.hub invoke:#"Send" withArgs:#[([imageData respondsToSelector:#selector(base64EncodedStringWithOptions:)] ? [imageData base64EncodedStringWithOptions:kNilOptions] : [imageData base64Encoding])]];
[self performSelector:#selector(sendText) withObject:nil afterDelay:0.5];
}
But sometimes it is working fine but sometimes text is posting first instead of image. No delegate method is called after completion of first..As we used different API for performing post method.
Please suggest any ideas where I am going wrong..Any alternatives for above..
Thanks in Advance..

IOS function handler threadsafe

The code below is called each time a scrollview scroll and if user scroll it multiple times, it crashed the code. How do i make sure only 1 code execute at a time or threadsafe?
[self.cv addInfiniteScrollingWithActionHandler:^{
[weakSelf loadNextPage];
}];
Here is example
- (void)_startExperiment {
FooClass *foo = [[FooClass alloc] init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 4; ++i) {
dispatch_async(queue, ^{
[foo doIt];
});
}
[foo release];
}
Detail is Here
The common pattern is to use a mutex to protect a critical section of code where the structure is accessed and/or modified.
just go through this link->
Does #synchronized guarantees for thread safety or not?

Resources