nsfetchedresultscontroller 中部分名称键路径的 Nil 关系值

Posted

技术标签:

【中文标题】nsfetchedresultscontroller 中部分名称键路径的 Nil 关系值【英文标题】:Nil relationship value for section name key path in nsfetchedresultscontroller 【发布时间】:2013-08-23 08:09:28 【问题描述】:

对于 EntityA 与 EntityB 具有多对一关系的核心数据模型,我想创建一个 EntityA 对象列表,按与之相关的 EntityB 的名称排序。通常要做到这一点,我会像这样设置获取请求:

if (_fetchedResultsController != nil) 
    return _fetchedResultsController;


NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

然后我会设置我的请求值:(在这种情况下,它是按物种名称排序的植物列表。有些植物没有设置物种。)

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Plant" inManagedObjectContext:self.managedObjectContext];
NSSortDescriptor *sortDescriptorOne = [[NSSortDescriptor alloc] initWithKey:@"species.name" ascending:YES];
NSString *sectionKeyPath = @"species.name";

然后我用通常的东西完成它:

[fetchRequest setEntity:entity];

// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];

NSArray *sortDescriptors = @[sortDescriptorOne];
[fetchRequest setSortDescriptors:sortDescriptors];

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:sectionKeyPath
cacheName:@"plantsCache"];

aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;

[NSFetchedResultsController deleteCacheWithName:@"plantsCache"];
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) 
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();


return _fetchedResultsController;

但是我得到的结果对我不起作用,因为这种关系是可选的。所以有些EntityA与EntityB有关系,有些则没有。当EntityA的relationship的值为nil时,结果控制器似乎不知道该怎么做。

任何建议我可以做些什么来继续使用关系的值制作部分但允许某些对象具有 nil ?

【问题讨论】:

EntityA 是植物,EntityB 是物种?那么一对多的关系是从 EntityB 到 EntityA,而不是像你的第一句话那样相反? - 与物种无关的植物实际上会发生什么?它们没有被提取,没有显示,......? 你说得对,我把它弄反了。我的意思是EntityA(植物)与EntityB(物种)具有多对一的关系。与任何物种无关的植物出现在空白(未命名)部分,核心数据向控制台抱怨。这种行为实际上是我想要的。但是,如果我从详细视图返回到列表视图,添加了一个没有物种的植物,列表将不会显示额外的植物项目,直到再次调用 fetchedresultscontroller。理想情况下,我不想在 viewWillAppear 上重新填充结果控件。 未命名部分问题(和投诉)也许可以通过将方法(或瞬态属性)sectionName 添加到 Plant 实体并将其用作 sectionNameKeyPath 来解决。该方法应返回self.species.name 或空字符串。 - 我目前无法对此进行测试,所以我不知道这是否真的有帮助。 谢谢!我会试试的。 【参考方案1】:

像这样在Plant 中设置一个临时属性:

-(NSString*)speciesName 
    return self.species ? self.species.name : @"";

您现在可以将speciesName 用作sectionNameKeyPath

【讨论】:

是否也需要在核心数据模型中或只是实体的类文件中? 需要在模型中。在模型编辑器中将其标记为“瞬态”。 (这正是我在上面的评论中所建议的。) - 我确实认为您必须将其定义为核心数据模型中的瞬态属性。实体类中的简单方法就足够了。 我发现了更多关于这个here的信息。瞬态属性作为部分键非常有用,但它不适用于排序描述符。只要瞬态属性的排序与排序属性相同(如果有意义的话),这似乎没问题。 FRC 只为每个获取的对象调用[object valueForKeyPath:keyPath],其中keyPath 是sectionNameKeyPath,如果存在这样的方法,KVC 会将其转换为[object keyPath]【参考方案2】:

在 Swift 3.0 中遇到了这个问题。有两个实体 EntityA 和 EntityB,其中 EntityA 通常属于(具有指向)某个 EntityB,但也可能是无主的。

Swift 的修复是向 EntityA 添加一个扩展,如果检测到 nil 条件,则返回一个空字符串。

extension Note 
    var bookTitleOrNil : String 
        if let bookTitle = self.book?.title 
            return bookTitle
        
        return ""
    

然后在构建 fetchRequestController (FRC) 时替换以下内容:

let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.coreDataManager.managedObjectContext, sectionNameKeyPath: "book.title", cacheName: nil)

使用 sectionNameKeyPath 的新扩展:

let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.coreDataManager.managedObjectContext, sectionNameKeyPath: "bookTitleOrNil", cacheName: nil)

然后,在为不同部分生成标题时,我使用了以下内容:

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
    let useIndexPath = IndexPath(row: 0, section: section) // Get first item in section (all notes in a section point to same book)
    let note = frc.object(at: useIndexPath)
    if let bookTitle = note.book?.title 
        return bookTitle
     else 
        return "Unlinked"
    

所以笔记首先按它们所属的书排序,如部分标题所示(尽管不属于任何书籍的笔记首先在“未链接”标题下排序)。

此外,在 FRC 的 fetchRequest 中设置排序时,需要设置主排序描述符以使用 book.title,然后设置任何其他排序,例如笔记的标题。

let sortDescriptor1 = NSSortDescriptor(key: "book.title", ascending: true)
let sortDescriptor2 = NSSortDescriptor(key: "title", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor1, sortDescriptor2]

当然,人们也可以设计应用程序,以便在创建便笺时通过额外检查使便笺始终属于一本书,从而避免出现 nil 情况。

【讨论】:

以上是关于nsfetchedresultscontroller 中部分名称键路径的 Nil 关系值的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?