当我使用UISearchBar
并将某些内容写为搜索字符串时,整个搜索内容会变得有点滞后。我的猜测是我在主线程中弄乱UI内容和Core Data但我可能错了。更重要的是,我使用一个实体来处理所有这些东西,所以没有任何关系等等。这个表中有3321个对象,这个应用程序正在消耗大约 12到14 MB的RAM 你可以看到什么在下面的屏幕截图中:
我认为它会更有效率,因为3321对象不是那么多。对于所有Core Data内容,我使用MagicalReacord。我在NSFetchedResultController
的一个实例上操作,但在主表视图和搜索表视图之间切换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];
}
我在一个thread中读到CONTAINS
可能消耗资源但我真的不知道如何以其他方式实现它。 我的另一个猜测是将此搜索放在另一个queue
并以异步方式执行,将其与用户界面分开...这不是那么迟钝,以至于用户界面必须等待多年重新加载表视图。但这是正确的方法吗?我必须改善这个UISearchBar
的表现,因为我不想让不满意的顾客
我希望您能提出一些想法或者我的代码有任何改进
答案 0 :(得分:11)
首先,CONTAINS
很慢。虽然这些信息对你的问题没有帮助,但最好还是知道你是在为了起步而奋斗。
其次,你在按下的每个字母上击中磁盘。这很浪费,当然也很慢。
您正在使用Magical Record,它似乎在每次按下时都会生成一个新的NSFetchedResultsController
。这很浪费,当然也很慢。
你应该怎么做?
在第一个字母上按下一个简单的NSFetchRequest
并保持批量大小,甚至可以保持提取限制。保留由此产生的NSArray
并使用它来显示结果。 是的,这会使您的UITableViewDataSource
更复杂。
在第二封及后续字母上按下,您可以针对现有NSArray
过滤。你不会回到磁盘。
如果检测到删除,则吹走阵列并从磁盘重建。
这会将磁盘命中数限制在第一个字母,并且当检测到删除时会大大增加搜索时间。
关于魔法记录。我对第三方框架有很多灰胡子的看法。我总是建议避免它们。避免与代码质量无关,它与尽可能靠近金属保持一致。 MR是核心数据之上的一层,我看不到它的价值。当然我也不喜欢点语法,所以我的意见很多:)
是的,您应该将第一个搜索结果存储在一个数组中,然后针对该数组显示。它会更快。
至于CONTAINS
;不确定你是否可以避免它。它很慢但它有效并且你正在进行字符串比较,所以你在这方面做的并不多。所以要解决其他问题,这样你就不会支付超出你需要的计算税。