我最近将CoreData支持的UITableView
切换为使用NSFetchedResultsController
而不是NSArray
。对于其中一个表格,滚动现在非常慢,我想我知道为什么,但我不知道解决这个问题的最佳解决方案是什么。
有两个实体Book
和Author
处于多对多关系中。每个单元格显示书名和作者。如果有多个作者,则只显示主作者。每个Author
都有一个“order”属性,该属性在导入数据时设置。
到目前为止我一直在做的是每次访问作者姓名时,我的Book
类都会返回一个mainAuthor属性(NSString
):
- (NSString *) mainAuthor
{
if (!mainAuthor)
{
NSSortDescriptor *sortOrder= [NSSortDescriptor sortDescriptorWithKey: @"order" ascending: YES];
NSArray *authorsSorted = [self.authors sortedArrayUsingDescriptors: @[sortOrder]];
Author *a = authorsSorted[0];
mainAuthor = [NSString stringWithFormat: @"%@", a.name];
}
return mainAuthor;
}
无论出于何种原因,现在多次调用它而不是仅调用一次并导致速度减慢。滚动表格时,NSFetchedResultsController
可能会反复提取引用吗?
那我怎么解决这个问题呢?一种可能性是使mainAuthor
成为属性而不是属性。因此,在导入数据时立即设置它。但是在我开始搞乱我的dataModel之前,我想知道这是否是前进的方法,或者是否有另一种解决方案?
更新1:这是我设置fetchController的代码:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSManagedObjectContext *moc = [NSManagedObjectContext MR_defaultContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName: @"Book"];
// sort the books by publishing date
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey: @"date" ascending: YES];
[fetchRequest setSortDescriptors: @[sort]];
// only get the books that belong to the library of the current viewController
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"libraries contains[cd] %@", self.library];
[fetchRequest setPredicate: predicate];
[fetchRequest setRelationshipKeyPathsForPrefetching: @[@"authors"]];
[fetchRequest setFetchBatchSize: 10];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest: fetchRequest
managedObjectContext: moc
sectionNameKeyPath: nil
cacheName: nil];
frc.delegate = self;
_fetchedResultsController = frc;
return _fetchedResultsController;
}
答案 0 :(得分:1)
根据您的示例代码,我假设您的核心数据架构中没有mainAuthor
。当Core Data处理托管对象的生命周期(故障)时,您应该将此属性添加到Book
实体,以通过使用transient属性来避免不可预测的结果。
尽管如此,我建议您返回一个Author
对象而不是NSString
,因为您可能会在以后更改该属性的名称,或者想要使用mainAuthor
的其他信息在你的用户界面。
瞬态属性
向您的mainAuthor
实体添加一个临时属性Book
,并为您的Book
类添加自定义访问者:
- (Author *)mainAuthor
{
[self willAccessValueForKey:@"mainAuthor"];
Author *value = [self primitiveValueForKey:@"mainAuthor"];
[self didAccessValueForKey:@"mainAuthor"];
if (value == nil)
{
NSPredicate *filterByMinOrder = [NSPredicate predicateWithFormat:@"order == %@.@min.order", self.authors];
value = [[self.authors filteredSetUsingPredicate:filterByMinOrder] anyObject];
[self setPrimitiveValue:value forKey:@"mainAuthor"];
}
return value;
}
使用瞬态的缺点是你必须确保数据在相应book
的生命周期内始终是最新的。因此,您必须重置mainAuthor
:
willTurnIntoFault
awakeFromFetch
awakeFromSnapshotEvents:
(可选,但如果用户可以更改数据则必须)
addAuthorObject:
removeAuthorObject:
致电[self setPrimitiveValue:nil forKey:@"mainAuthor"]
。
提示:创建合成primitiveMainAuthor
而不是使用primitiveValue:forKey:
越来越好:Managed Object Accessor Methods
您是否尝试在fetchBatchSize
的{{1}}中设置NSFetchedResultsController
? Docs: NSFetchRequest fetchBatchSize
是的,在这种情况下,必须在fetchRequest
中设置适当的关系。
要识别瓶颈,设置调试参数setRelationshipKeyPathsForPrefetching
以查看Core Data创建的SQL语句也非常有用。这通常也有助于理解不同的-com.apple.CoreData.SQLDebug 1
属性及其影响。