我最近将支持CoreData的UITableView切换为使用NSFetchedResultsController而不是NSArray。对于其中一张表,滚动现在非常缓慢,我想我知道为什么,但是我还不知道什么是解决此问题的最佳解决方案。

有两个实体BookAuthor处于多对多关系。每个单元格均显示书名以及作者。如果作者不止一个,它将只显示主要作者。每个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



更新资料

您是否尝试过在fetchBatchSizeNSFetchedResultsController中设置fetchRequestDocs: NSFetchRequest fetchBatchSize



更新2

是的,在这种情况下,必须在setRelationshipKeyPathsForPrefetching中设置适当的关系。

要确定瓶颈,设置调试参数-com.apple.CoreData.SQLDebug 1以查看由Core Data创建的SQL语句也非常有帮助。这通常还有助于了解不同的NSFetchRequest属性及其影响。

10-08 15:44