如何判断我的 NSManagedObject 是不是驻留在只读 NSPersistentStore 中?

Posted

技术标签:

【中文标题】如何判断我的 NSManagedObject 是不是驻留在只读 NSPersistentStore 中?【英文标题】:How Can I Tell If My NSManagedObject Resides in a Read Only NSPersistentStore?如何判断我的 NSManagedObject 是否驻留在只读 NSPersistentStore 中? 【发布时间】:2020-02-27 16:58:07 【问题描述】:

我想将只读示例/教程数据添加到我的基于 Core Data 的 macOS 应用程序中。

我将在包含示例数据的应用程序包中包含一个 SQL 文件。我的 NSPersistentContainer 将有 2 个 NSPersistentStore,一个可写,一个只读。我的模型只有一个默认配置,因为两家商店的模型相同。

我的 UI 需要知道显示的数据是否为只读数据,例如,以阻止该数据可拖动。

我知道 NSManagedObject 不支持只读状态,请参阅和:Is it possible to return NSManagedObjects as read-only in Core Data? ...和文档。

我认为最好的方法是向我的 NSManagedObject 派生类添加一个只读属性,可以在必要时进行查询。但是,我看不到如何轻松设置此属性!我找不到从 NSManagedObject 到 NSPersistentStore 的直接链接。

我可以设置一个 NSFetchRequest 并指定只读存储并查看 NSManagedObject 是否在其中,但这似乎有点荒谬。

请问我在这里遗漏了什么更明显的东西吗?

【问题讨论】:

大声思考:您能否使用一次性的临时上下文从只读存储中提取,以检索相关的 objectID,并与 awakeFromFetch 中的该列表进行比较以设置您的只读属性? 谢谢@pbasdf。这可能是相当合理的,因为只读项的集合不会很大。在那里我假设可能有一些简单的事情! 【参考方案1】:

感谢 pbasdf 的建议...

我找不到直接的方法来实现这一点。我不得不放弃使用NSPersistentContainer 来简化我的核心数据堆栈。但是,如果您需要将图形的一小部分设置为只读,我认为这是一个相当优雅的解决方案。

我将NSPersistentStoreCoordinator 子类化以缓存添加到其中的任何只读存储的NSManagedObjectIDs

class GraphStoreCoordinator: NSPersistentStoreCoordinator

    override init(managedObjectModel model: NSManagedObjectModel)
    
        readOnlyTestContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        super.init(managedObjectModel: model)
        readOnlyTestContext.persistentStoreCoordinator = self

        NotificationCenter.default
            .addObserver(forName: .NSPersistentStoreCoordinatorStoresDidChange,
                         object: self, queue: nil)   [unowned self] notification in

            // userInfo will be in this form for add/remove keys - not supporting migration here
            guard let userInfo = notification.userInfo as? [String: [NSPersistentStore]] else 
                unhandledError("Invalid userInfo for NSPersistentStoreCoordinatorStoresDidChange.") 

            userInfo[NSAddedPersistentStoresKey]?.forEach  self.didAddStore($0) 
            userInfo[NSRemovedPersistentStoresKey]?.forEach  self.didRemoveStore($0) 
        
    

    deinit 
        NotificationCenter.default
            .removeObserver(self, name: .NSPersistentStoreCoordinatorStoresDidChange, object: self)
    

    private func didAddStore(_ store: NSPersistentStore) 
        guard store.isReadOnly else  return 

        var addedObjects = Set<NSManagedObjectID>()
        baseEntityNames.forEach  entityName in
            let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: entityName)
            fetchRequest.affectedStores = [store]

            do 
                let addedEntityObjects = try readOnlyTestContext.fetch(fetchRequest)
                addedObjects = addedObjects.union(addedEntityObjects.map  $0.objectID )
             catch 
                unhandledError("Failed to fetch all \(entityName) for read only check: \(error)") 
        

        readOnlyObjects[store.identifier] = addedObjects
    

    private func didRemoveStore(_ store: NSPersistentStore) 
        guard store.isReadOnly else  return 
        readOnlyObjects.removeValue(forKey: store.identifier)
    

    /// Returns the minimum set of entities that can be fetched for readonly checking
    private lazy var baseEntityNames: [String] = 
        return managedObjectModel.entitiesByName.compactMap  $1.superentity == nil ? $0 : nil 
    ()

    private var readOnlyTestContext: NSManagedObjectContext

    /// Readonly objectIDs keyed per persistent store
    private var readOnlyObjects = [String : Set<NSManagedObjectID>]()

    internal func isObjectReadOnly(_ objectID: NSManagedObjectID) -> Bool 
        return readOnlyObjects.contains(where:  $1.contains(objectID)  )
    

然后我向NSManagedObject 添加了一个扩展名,以查询其NSPersistentStoreCoordinator 的只读状态:

public extension NSManagedObject

    /// Does this managed object reside in a read-only persistent store?
    var isReadOnly: Bool 
        guard let coordinator = managedObjectContext?
            .persistentStoreCoordinator as? GraphStoreCoordinator else 
            unhandledError("Should only check readonly status in a GraphStoreCoordinator") 

        return coordinator.isObjectReadOnly(objectID)
    

【讨论】:

以上是关于如何判断我的 NSManagedObject 是不是驻留在只读 NSPersistentStore 中?的主要内容,如果未能解决你的问题,请参考以下文章

如何观察 NSManagedObject 是不是从 managedObjectContext 中移除

如何确定 NSManagedObject 是不是是永久的?

如何在 UILocalNotification.userInfo 中设置 NSManagedObject 信息?

NSManagedObject 传递给 ViewController 是不是反映所有更新

嵌套 NSManagedObject 是不是正确/安全?

使用 CoreData,我如何确定 NSManagedObject 是不是在特定上下文中?