核心数据的UISearchBar性能问题

当我使用UISearchBar并将某些内容写入searchstring时, 整个search事件会变得有点迟缓 。 我的猜测是我在主线程中搞乱了UI的东西和核心数据,但是我可能是错的。 更重要的是,我为所有这些东西使用一个实体,所以没有关系等等。这个表中有3321个对象,这个应用程序大约需要12到14 MB的RAM ,你可以在下面的截图中看到:

RAM使用情况

我认为这样会更有效率,因为3321个对象不是那么多。 对于所有核心数据的东西我使用MagicalReacord 。 我操作NSPredicate一个实例,但在主表视图和search表视图之间切换NSPredicate
但是,没有什么比这更有价值的源代码,所以在这里你去:

 #import "GroupsViewController.h" #import "CashURLs.h" #import "Group.h" #import <AFNetworking.h> @interface GroupsViewController () { NSPredicate *resultPredicate; UIRefreshControl *refreshControl; } @property (strong, nonatomic) NSMutableArray *selectedGroups; @property (strong, nonatomic) NSFetchedResultsController *groupsFRC; @end @implementation GroupsViewController -(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // Getting array of selected groups self.selectedGroups = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedGroups"]]; } - (void)viewDidLoad { [super viewDidLoad]; resultPredicate = nil; // Initializing pull to refresh refreshControl = [[UIRefreshControl alloc] init]; [refreshControl addTarget:self action:@selector(refreshData) forControlEvents:UIControlEventValueChanged]; [self.tableView addSubview:refreshControl]; // Check if there is at least one Group entity in persistat store if (![Group MR_hasAtLeastOneEntity]) { [self refreshData]; } else { [self refreshFRC]; [self.tableView reloadData]; } } #pragma mark - Downloading -(void)refreshData { // Show refresh control [refreshControl beginRefreshing]; // On refresh delete all previous groups (To avoid duplicates and ghost-groups) [Group MR_truncateAll]; [[AFHTTPRequestOperationManager manager] GET:ALL_GROUPS parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { // For each group from downloaded JSON... for (id group in responseObject) { // ... Create entity and filll it with data Group *groupEntity = [Group MR_createEntity]; groupEntity.name = [group valueForKey:@"name"]; groupEntity.cashID = [group valueForKey:@"id"]; groupEntity.sectionLetter = [[[group valueForKey:@"name"] substringToIndex:1] uppercaseString]; groupEntity.caseInsensitiveName = [[group valueForKey:@"name"] lowercaseString]; } // Save Groups to persistent store [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait]; [self refreshFRC]; [self.tableView reloadData]; [refreshControl endRefreshing]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"Failed to load data: %@", [error localizedDescription]); // End refreshing [refreshControl endRefreshing]; // Show alert with info about internet connection UIAlertView *internetAlert = [[UIAlertView alloc] initWithTitle:@"Ups!" message:@"Wygląda na to, że nie masz połączenia z internetem" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [internetAlert show]; }]; } #pragma mark - Table View -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Count sections in FRC return [[self.groupsFRC sections] count]; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Count groups in each section of FRC return [[[self.groupsFRC sections] objectAtIndex:section] numberOfObjects]; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // Get reusable cell UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"GroupCell"]; // If there isn't any create new one if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"GroupCell"]; } Group *group = [self.groupsFRC objectAtIndexPath:indexPath]; cell.textLabel.text = group.name; // Checking if group has been selected earlier if ([self.selectedGroups containsObject:@{@"name" : group.name, @"id" : group.cashID}]) { [cell setAccessoryType:UITableViewCellAccessoryCheckmark]; } else { [cell setAccessoryType:UITableViewCellAccessoryNone]; } return cell; } // Adding checkmark to selected cell -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath]; Group *group = [self.groupsFRC objectAtIndexPath:indexPath]; // Checking if selected cell has accessory view set to checkmark and add group to selected groups array if (selectedCell.accessoryType == UITableViewCellAccessoryNone) { selectedCell.accessoryType = UITableViewCellAccessoryCheckmark; [self.selectedGroups addObject:@{@"name" : group.name, @"id" : group.cashID}]; NSLog(@"%@", self.selectedGroups); } else if (selectedCell.accessoryType == UITableViewCellAccessoryCheckmark) { selectedCell.accessoryType = UITableViewCellAccessoryNone; [self.selectedGroups removeObject:@{@"name" : group.name, @"id" : group.cashID}]; NSLog(@"%@", self.selectedGroups); } // Hiding selection with animation for nice and clean effect [tableView deselectRowAtIndexPath:indexPath animated:YES]; } #pragma mark - Filtering/Searching // Seting searching predicate if there are any characters in search bar -(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { if (searchText.length > 0) { resultPredicate = [NSPredicate predicateWithFormat:@"SELF.caseInsensitiveName CONTAINS[c] %@", searchText]; } else { resultPredicate = nil; } [self refreshFRC]; } -(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { // If user cancels searching we set predicate to nil resultPredicate = nil; [self refreshFRC]; } // Refreshing NSFetchedResultController - (void)refreshFRC { self.groupsFRC = [Group MR_fetchAllSortedBy:@"caseInsensitiveName" ascending:YES withPredicate:resultPredicate groupBy:@"sectionLetter" delegate:self]; } 

我在一个线程中读到, CONTAINS可能会消耗资源,但我真的不知道如何以其他方式实现它。 我的另一个猜测是把这个search放在另一个queue并且是asynchronous执行的 ,将它从UI中分离出来…… UI不需要等待很长时间才能重新载入表视图。 但这是正确的吗? 我不得不提高这个UISearchBar因为我不想有不愉快的客户。
我希望你能提出一些想法,或者对我的代码有任何改进

首先, CONTAINS速度很慢。 虽然这些信息并不能帮助你解决问题,但知道你正在与山头作斗争是一件好事。

其次,每按下一个字母,你都会碰到磁盘。 这太浪费了,当然很慢。

你正在使用魔法logging似乎是build立一个新的NSFetchedResultsController在每个字母按下。 这太浪费了,当然很慢。

你该怎么办?

在第一个字母按下做一个简单的NSFetchRequest并保持批量大小,甚至可能保持取下限制。 保留出来的NSArray ,并用它来显示结果。 是的,这会让你的UITableViewDataSource更复杂。

在第二个和后来的字母按你筛选现有的NSArray 。 你不会回到磁盘。

如果检测到删除,则吹走arrays并从磁盘重build。

这将限制您的磁盘命中只是第一个字母,当检测到删除将大大增加您的search时间。

更新

关于魔法logging。 我对第三方框架非常灰心。 我总是build议避免他们。 这种回避与代码质量没有任何关系,它必须尽可能靠近金属。 MR是核心数据的顶层,我没有看到价值。当然,我也不喜欢点语法,所以采取我的意见,一粒盐:)

是的,您应该将第一个search结果存储在数组中,然后显示在该数组中。 它会更快。

至于CONTAINS ; 不知道你是否可以避免它。 它是缓慢的,但它的工作,你正在做string比较,所以没有太多,你可以在这方面做。 因此,修理其他的东西,以免付出更多的计算税。