【问题标题】:How to use a predicate for one-to-many managed objects in Core Data如何在 Core Data 中为一对多托管对象使用谓词
【发布时间】:2020-11-23 17:39:06
【问题描述】:

我目前有两个具有一对多关系的 Core Data 托管对象。

目标

extension Goal {

    @nonobjc public class func createFetchRequest() -> NSFetchRequest<Goal> {
        return NSFetchRequest<Goal>(entityName: "Goal")
    }

    @NSManaged public var title: String
    @NSManaged public var date: Date
    @NSManaged public var progress: NSSet?

}

进展

extension Progress {

    @nonobjc public class func createFetchRequest() -> NSFetchRequest<Progress> {
        return NSFetchRequest<Progress>(entityName: "Progress")
    }

    @NSManaged public var date: Date
    @NSManaged public var comment: String?
    @NSManaged public var goal: Goal

}

对于每个目标,您可以有多个 Progress 对象。问题是,当我以特定的Goal 作为谓词请求获取Progress 时,没有返回任何内容。我怀疑我没有正确使用谓词。

这就是我要求他们的方式。

  1. 首先,我为表视图控制器获取Goal
var fetchedResultsController: NSFetchedResultsController<Goal>!

if fetchedResultsController == nil {
    let request = Goal.createFetchRequest()
    let sort = NSSortDescriptor(key: "date", ascending: false)
    request.sortDescriptors = [sort]
    request.fetchBatchSize = 20
    
    fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.context, sectionNameKeyPath: "title", cacheName: nil)
    fetchedResultsController.delegate = self
}

fetchedResultsController.fetchRequest.predicate = goalPredicate

do {
    try fetchedResultsController.performFetch()
} catch {
    print("Fetch failed")
}
  1. 并将结果传递到下一个屏幕,Detail 视图控制器:
if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController {
    vc.goal = fetchedResultsController.object(at: indexPath)
    navigationController?.pushViewController(vc, animated: true)
}
  1. 最后,我使用Goal 作为Detail 视图控制器的谓词来获取Progress
var goal: Goal!
let progressRequest = Progress.createFetchRequest()
progressRequest.predicate = NSPredicate(format: "goal == %@", goal)

if let progress = try? self.context.fetch(progressRequest) {
    print("progress: \(progress)")

    if progress.count > 0 {
        fetchedResult = progress[0]
        print("fetchedResult: \(fetchedResult)")
    }
}

Goal 已正确返回,但 Progress 没有得到任何返回。我试过了:

progressRequest.predicate = NSPredicate(format: "goal.title == %@", goal.title)

progressRequest.predicate = NSPredicate(format: "ANY goal == %@", goal)

但结果还是一样。

以下是我建立关系的方式:

// input for Progress from the user
let progress = Progress(context: self.context)
progress.date = Date()
progress.comment = commentTextView.text

// fetch the related Goal
var goalForProgress: Goal!
let goalRequest = Goal.createFetchRequest()
goalRequest.predicate = NSPredicate(format: "title == %@", titleLabel.text!)

if let goal = try? self.context.fetch(goalRequest) {
    if goal.count > 0 {
        goalForProgress = goal[0]
    }
}

// establish the relationship between Goal and Progress
goalForProgress.progress.insert(progress)

// save
if self.context.hasChanges {
    do {
        try self.context.save()
    } catch {
        print("An error occurred while saving: \(error.localizedDescription)")
    }
}

【问题讨论】:

  • var goal: Goal! 声明了变量但没有设置
  • 我正在传递上一个屏幕中的值vc.goal = fetchedResultsController.object(at: indexPath),并将其用作谓词。
  • 对不起,我的错,你在哪里运行 DetailViewController 中的提取代码?而不是try? 捕获错误。
  • 不用担心。我在viewDidLoad() 中运行获取代码。

标签: ios swift core-data nspredicate nsmanagedobject


【解决方案1】:

实际上您不需要重新获取数据。你可以从关系中获得进步

  • progress 声明为本机Set

    @NSManaged public var progress: Set<Progress>
    
  • DetailViewController中删除viewDidLoad中的fetch代码并声明

    var progress: Progress!
    
  • 在第一个视图控制器中过滤进度

    let goal = fetchedResultsController.object(at: indexPath)
    if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController,
       let progress = goal.progress.first(where: {$0.goal.title == goal.title}) {
        vc.progress = progress
        navigationController?.pushViewController(vc, animated: true)
    }
    

并考虑以复数形式命名一对多关系(progresses

【讨论】:

  • 感谢您的回答。它给了我一个Progress 没有title 的错误,所以我将其更改为$0.goal.title ,但是现在当我单击表格的单元格时,单元格会突出显示,但不会导航到Detail 出于某种原因查看控制器。
  • 对,我更新了答案。然后检查模型中的关系是否设置正确。
  • 我更新了我的问题,包括我如何建立关系。在保存上下文之前,我先获取目标,然后获取 insert(_:at: ) Progress 对象。
  • 我指的是核心数据模型中的关系,尤其是反转。请使用调试器,设置断点和观察变量。
  • 哦,我明白了。我有彼此的反转。 progress 具有 goal 作为反转,goal 具有 progress 作为反转。
【解决方案2】:

我发现这是由于 Core Data Fault 导致 Core Data 延迟加载数据,除非您显式访问数据,否则不会显示该值。

您可以执行以下操作:

let goal = fetchedResultsController.object(at: indexPath)
if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController,
    let progress = goal.progress.first(where: {$0.goal.title == goal.title}) {
    vc.goalTitle = goal.title
    vc.date = progress.date
    
    if let comment = progress.comment {
        vc.comment = comment
    }
    
    navigationController?.pushViewController(vc, animated: true)
}

或将returnsObjectsAsFaults 设置为false

这是一个很好的 article 主题。

【讨论】:

    猜你喜欢
    • 2011-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-02
    • 1970-01-01
    相关资源
    最近更新 更多