Reputation: 1790
I am having a problem with the loading time of my tableview, how can I improve this? It takes about 25-30 seconds to load a tableview with about 90000+ rows. I have about 130000 rows in my sqlite database and I use core data access that data.
Any help or suggests would be greatly appreciated.
Thank you for your time and if you need any more information please let me know.
Two things I've been trying to optimise.
+Core Data - maybe my fetching of the data can be improved, but I don't know what else I can do, code listed below.
if (fetchedResultsController != nil)
{
return fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:@"DataModel" inManagedObjectContext:_context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"subsection" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
//[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"section == [c] %@ AND area == 'Tables'", tablesSection]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"area == 'Tables'"]];
[fetchRequest setFetchBatchSize:100];
//[fetchRequest setFetchLimit:100];
[fetchRequest setFetchOffset:10];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:_context sectionNameKeyPath:@"uppercaseFirstLetterOfNameTables" cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
fetchedResultsController.delegate = self;
return fetchedResultsController;
+The tableview, I think the way I'm loading the data is probably the main issue here. I dynamically calculate the height for each cell and I think this slows things down, if I remove the code for the method heightForRowAtIndexPath, I can get loading time down to 8 second.
(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
Model *info;
if(self.searchDisplayController.isActive)
{
info = [searchFetchedResultsController objectAtIndexPath:indexPath];
}
else
{
info = [fetchedResultsController objectAtIndexPath:indexPath];
}
NSString *subsectionHeight = info.subsection;
NSString *textHeight = info.text;
CGSize titleSize = [subsectionHeight sizeWithFont:[UIFont fontWithName:@"Helvetica-Bold" size:20] constrainedToSize:CGSizeMake(300, MAXFLOAT) lineBreakMode:UILineBreakModeWordWrap];
CGSize detailSize = [textHeight sizeWithFont:[UIFont fontWithName:@"Helvetica-Bold" size:16] constrainedToSize:CGSizeMake(300, MAXFLOAT) lineBreakMode:UILineBreakModeWordWrap];
return detailSize.height+titleSize.height;
//return 88;
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 0;
cell.detailTextLabel.numberOfLines = 0;
}
//if-statement need to prevent search index issue
if(self.searchDisplayController.isActive && [self.searchDisplayController.searchBar.text isEqualToString:@""])
{
return cell;
}
// Configure the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
(void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Model *info;
if (self.searchDisplayController.isActive)
{
info = [searchFetchedResultsController objectAtIndexPath:indexPath];
}
else
{
info = [fetchedResultsController objectAtIndexPath:indexPath];
}
cell.textLabel.text = info.subsection;
cell.detailTextLabel.text = info.text;
}
UPDATE: I am using the solution with heightForRowAtIndexPath commented out, but I need help with search optimizing, could someone help me understand how to implement the solution in WWDC 2010 session 137 Optimizing Core Data Performance, I don't understand the creating a new entity part, e.g. do I need to populate that new entity with data?
Upvotes: 2
Views: 1077
Reputation: 1359
If all you need to populate your UITableViewCell are the attributes "text" and "subsection", then you could split your main table in one table that just has these 2 attributes and maybe a key field and put all other attributes in another table, linked one-on-one. If all records are read into memory (which should not happen normally when you fire up a UITableview) then it will take much less time if the table is smaller (i.e. does contain only a few columns). I moved an image field off my main table while leaving about 5 fields and 5 relations in the main table, and had no problems with 150.000 records on an iPad 2.
But you should be testing what really happens by logging the actual SQL statements that Core Data is using (By passing -com.apple.CoreData.SQLDebug 1 as a parameter on launch).
If you app is behaving well, only the records shown initially should fire the heightForRowAtIndexPath method. So not 130.000 times, but max. 10 times to show the first results. So that should normally not be your problem.
Pagination could also be a solution. But in normal situations, core data would handle pagination automatically behind the scenes. Just setting fetchBatchSize should be enough for that to happen automatically.
Upvotes: 0
Reputation: 14427
You definitely don't need to be calling heightForRowAtIndexPath 9000 times, just set the height in IB to the height you need and take that method out. 8 seconds for 9000 records isn't that bad actually.
9000 records is A LOT for a TableView to handle. Is there anyway you can reduce this #?
Upvotes: 0
Reputation: 1164
you must paginate your tableview :put a button that show more results on the end of the tableview (i can't imagine my self scrolling on 9000 rows)
Upvotes: 1