将 ManagedObject 集合从获取结果传递到闭包 ViewController

Passing ManagedObject collection from fetch result to closure up to ViewController

我想执行后台提取并将结果传递给闭包。目前我正在使用 NSPersistentContainer 中的 performBackgroundTask 方法,它给出了 NSManagedObjectContext 作为闭包。然后使用该上下文我正在执行获取请求。获取完成后,我将结果传递给完成处理程序。

func getAllWorkspacesAsync(completion: @escaping ([Workspace]) -> Void) {
    CoreDataStack.shared.databaseContainer.performBackgroundTask { childContext in
        let workspacesFetchRequest: NSFetchRequest<Workspace> = NSFetchRequest(entityName: "Workspace")
        workspacesFetchRequest.predicate = NSPredicate(format: "remoteId == %@", "\(UserDefaults.lastSelectedWorkspaceId))")
        do {
            let workspaces: [Workspace] = try childContext.fetch(workspacesFetchRequest)
            completion(workspaces)
        } catch let error as NSError {
            // Handle error
        }
    }
}

我将从 ViewModel 调用此方法并使用 Combine PassthroughSubject 通知 ViewController 事件。

class WorkspaceViewModel {

    private var cancellables: Set<AnyCancellable> = []
    let resultPassthroughObject: PassthroughSubject<[Workspace], Error> = PassthroughSubject()

    private let cacheManager = WorkspaceCacheProvider.shared

    static public let shared: WorkspaceViewModel = {
        let instance = WorkspaceViewModel()
        return instance
    }()

    func fetchWorkspaces() {
        cacheManager.getAllWorkspacesAsync { [weak self] workspaces in
            guard let self = self else { return }
            self.resultPassthroughObject.send(workspaces)
        }
    }
}

和 ViewController 代码:

class WorkspaceViewController: UIViewController {

    private var cancellables: Set<AnyCancellable> = []

    override func viewDidLoad() {
        super.viewDidLoad()
    
        WorkspaceViewModel.shared.resultPassthroughObject
            .receive(on: RunLoop.main)
            .sink { _ in } receiveValue: { workspaces in
            // update the UI
        }
        .store(in: &cancellables)

    }
}

我的问题是:传递 NSManagedObject 项目安全吗?

没有,it is not

Do not pass NSManagedObject instances between queues. Doing so can result in corruption of the data and termination of the app. When it is necessary to hand off a managed object reference from one queue to another, use NSManagedObjectID instances.

你会想要这样的东西:

func getAllWorkspacesAsync(completion: @escaping ([NSManagedObjectID]) -> Void) {
    CoreDataStack.shared.databaseContainer.performBackgroundTask { childContext in
        let workspacesFetchRequest: NSFetchRequest<Workspace> = NSFetchRequest(entityName: "Workspace")
        workspacesFetchRequest.predicate = NSPredicate(format: "remoteId == %@", "\(UserDefaults.lastSelectedWorkspaceId))")
        do {
            let workspaces: [Workspace] = try childContext.fetch(workspacesFetchRequest)
            completion(workspaces.map { [=10=].objectID } )
        } catch let error as NSError {
            // Handle error
        }
    }
}

您需要对您的发布商进行相应的更改,以便发布 [NSManagedObjectID]

在视图控制器中收到此数组后,您需要在视图上下文中调用 object(with:) 以获取 Workspace 本身。无论如何,这将在对象的视图上下文中执行提取。

您可能需要考虑获取工作区所花费的时间是否值得使用后台上下文。默认情况下,Core Data 对象作为 faults 检索,并且仅在通过访问对象的属性触发故障时才获取实际属性值。

或者,如果视图控制器的目的是呈现用户可以从中 select 的工作区列表,您可以 return 包含工作区名称和对象 ID。然而,这一切对我来说听起来像 pre-optimisation。