我最近将支持CoreData的UITableView
切换为使用NSFetchedResultsController
而不是NSArray
。对于其中一张表,滚动现在非常缓慢,我想我知道为什么,但是我还不知道什么是解决此问题的最佳解决方案。
有两个实体Book
和Author
处于多对多关系。每个单元格均显示书名以及作者。如果作者不止一个,它将只显示主要作者。每个Author
都有一个“订单”属性,该属性在导入数据时设置。
到目前为止,我一直在做的是每次访问作者姓名时,我的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;
}
最佳答案
根据您的示例代码,我假设mainAuthor
不在您的Core Data模式中。当Core Data处理托管对象的生存期(故障)时,应将此属性添加到Book
实体,以免使用过渡属性来避免不可预测的结果。
尽管如此,我还是建议返回一个Author
对象而不是NSString
,因为您将来可能会更改属性名称,或者想在UI中使用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 == %@[email protected]", 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
的NSFetchedResultsController
中设置fetchRequest
? Docs: NSFetchRequest fetchBatchSize更新2
是的,在这种情况下,必须在
setRelationshipKeyPathsForPrefetching
中设置适当的关系。要确定瓶颈,设置调试参数
-com.apple.CoreData.SQLDebug 1
以查看由Core Data创建的SQL语句也非常有帮助。这通常还有助于了解不同的NSFetchRequest
属性及其影响。