使用 NSFetchedResultsController 从数据库中获取导致应用程序冻结
Posted
技术标签:
【中文标题】使用 NSFetchedResultsController 从数据库中获取导致应用程序冻结【英文标题】:Fetching from DB using NSFetchedResultsController causes app freezes 【发布时间】:2015-04-09 12:19:32 【问题描述】:在我的应用程序中处理来自服务器的大数据时出现冻结问题。我真的对这个问题感到困惑,最后我尝试使用父子策略在后台保存核心数据。之后,该应用程序变得比以前快一点,但仍然存在小冻结。所以我认为现在问题可能是因为获取核心数据。那么有什么方法可以在后台获取 coredata 吗? 我已经搜索过链接 NSFetchedResultsController: Fetch in a background thread
但是这些链接对我没有帮助。
-(NSFetchedResultsController*)fetchedResultsController
if (_fetchedResultsController != nil)
return _fetchedResultsController;
NSString *loginUser=[[NSUserDefaults standardUserDefaults] valueForKey:@"currentUser"];
AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"ThreadInfo"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:@"threadDate" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:@"userEmail == %@",loginUser];
// NSPredicate *providerPredicate = [NSPredicate predicateWithFormat:@"isReceiver == YES"];
// NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: @[threadPredicate, providerPredicate]];
[fetchRequest setPredicate:threadPredicate];
[fetchRequest setFetchBatchSize:5];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context sectionNameKeyPath:nil
cacheName:nil];
_fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
Appdelegate
- (void)saveContext
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil)
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
#pragma mark - Core Data stack
- (NSManagedObjectContext *)managedObjectContext
if (_managedObjectContext != nil)
return _managedObjectContext;
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
// _managedObjectContext = [[NSManagedObjectContext alloc] init];
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
if (_managedObjectModel != nil)
return _managedObjectModel;
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"IXCoreDataModel" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
if (_persistentStoreCoordinator != nil)
return _persistentStoreCoordinator;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Inxed.sqlite"];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
/*
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
return _persistentStoreCoordinator;
#pragma mark - Application's Documents directory
// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
在冻结时,我通过暂停应用程序在该行(下方)中发现了问题。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
ThreadInfo *info=[_fetchedResultsController objectAtIndexPath:indexPath];// THIS LINE TAKES TOO MUCH TIME
if([info.isSystemMessage boolValue])
return 178+90+25;
else
return 300;
【问题讨论】:
对你没有帮助吗?您是否进行了任何分析以发现您的性能问题? 用导致冻结的行更新了问题。 请查看***.com/questions/14803205/… 我已经检查过该代码,但你知道我是新手,这让我更加困惑。我想要的只是如何修改“fetchedResultsController”方法来处理后台获取? 不再需要后台线程。考虑使用估计的高度。 【参考方案1】:您正在处理大数据,但同时获取所有数据并且不要将其限制为有用的数量。所以在初始化你的NSFetchedResultsController
时,你可能不需要获取真实的对象,使用错误,它们大部分都可以。
// FRC request
NSFetchRequest *fetchRequest = [NSFetchRequest new];
fetchRequest.returnsObjectsAsFaults = YES;// redundant, I only want to make this clear
fetchRequest.fetchBatchSize = 20; // or even less, depending on your amount of data visible to the user
然后,作为Wain said,使用estimated height
。这允许 FRC 猜测您要查看的数据的粗略部分,并且当计算出实际高度时(如您所做的那样),数据就在手边。
接下来的事情:索引数据。您的isSystemMessage
是否已编入索引?应该是的。
【讨论】:
以上是关于使用 NSFetchedResultsController 从数据库中获取导致应用程序冻结的主要内容,如果未能解决你的问题,请参考以下文章
在 Swift 3 中难以配置 NSFetchedResultsController