CoreData:当 NSManagedObject 更改时收到通知,而不保留对 NSManagedObject 的引用

Vla*_*lad 1 macos core-data ios

我想观察特定的变化NSManagedObject并相应地更新 UI。

\n\n

不想保留引用,NSManagedObject因为它可能随时被删除(即通过远程推送通知的结果)。

\n\n

目前我正在设置NSFetchRequest并实现这一目标NSFetchedResultsControllerNSFetchedResultsControllerDelegate但想要简化这个解决方案(见下文)。

\n\n

有没有什么简单的方法可以在NSManagedObject不使用 的情况下观察变化NSFetchedResultsControllerDelegate

\n\n

谢谢你!

\n\n

示例代码 (Xcode Playground)\n

\n\n
import PlaygroundSupport\nimport Cocoa\nimport CoreData\n\nPlaygroundPage.current.needsIndefiniteExecution = true\n\nextension NSManagedObject {\n\n   public static var entityName: String {\n      let className = String(describing: self)\n      return className.components(separatedBy: ".").last!\n   }\n\n   public convenience init(in context: NSManagedObjectContext) throws {\n      let entityName = type(of: self).entityName\n      guard let entityDescription = NSEntityDescription.entity(forEntityName: entityName, in: context) else {\n         fatalError()\n      }\n      self.init(entity: entityDescription, insertInto: context)\n   }\n}\n\n@objc(UserInfoEntity)\nclass UserInfoEntity: NSManagedObject {\n\n   @NSManaged var id: Int64\n   @NSManaged var name: String\n\n   convenience init(id: Int64, name: String, in context: NSManagedObjectContext) throws {\n      try self.init(in: context)\n      self.id = id\n      self.name = name\n   }\n}\n\nclass DBStack {\n\n   static let shared = DBStack()\n   static var mainContext: NSManagedObjectContext {\n      return shared.mainContext\n   }\n\n   private typealias PSC = NSPersistentStoreCoordinator\n   private lazy var coordinator: PSC = PSC(managedObjectModel: self.model)\n   private lazy var model: NSManagedObjectModel = self.setupModel()\n   private lazy var writerContext: NSManagedObjectContext = self.setupWriterContext()\n   private lazy var mainContext: NSManagedObjectContext = self.setupMainContext()\n   private var isInitialized = false\n\n   init() {\n   }\n\n   func setupInMemoryStore() throws {\n      guard !isInitialized else { return }\n      isInitialized = true\n      try coordinator.addPersistentStore(ofType: NSInMemoryStoreType,\n                                         configurationName: nil, at: nil, options: nil)\n   }\n\n   static func makeChildContext() -> NSManagedObjectContext {\n      let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)\n      moc.parent = mainContext\n      return moc\n   }\n\n   private func setupWriterContext() -> NSManagedObjectContext {\n      let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)\n      moc.persistentStoreCoordinator = coordinator\n      return moc\n   }\n\n   private func setupMainContext() -> NSManagedObjectContext {\n      let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)\n      moc.parent = writerContext\n      return moc\n   }\n\n   private func setupModel() -> NSManagedObjectModel {\n\n      let attributeID = NSAttributeDescription()\n      attributeID.name = "id"\n      attributeID.attributeType = .integer64AttributeType\n      attributeID.isOptional = false\n      attributeID.isIndexed = true\n\n      let attributeName = NSAttributeDescription()\n      attributeName.name = "name"\n      attributeName.attributeType = .stringAttributeType\n      attributeName.isOptional = false\n\n      let entityUserInfo = NSEntityDescription()\n      entityUserInfo.name = "UserInfoEntity"\n      entityUserInfo.managedObjectClassName = "UserInfoEntity"\n      entityUserInfo.properties = [attributeID, attributeName]\n\n      let model = NSManagedObjectModel()\n      model.entities = [entityUserInfo]\n      return model\n   }\n}\n\nclass FetchedResultsDelegate: NSObject, NSFetchedResultsControllerDelegate {\n\n   public var entityChanged: ((Void) -> Void)?\n\n   public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {\n      entityChanged?() // Notify about content change.\n   }\n}\n\n// Create or Update user info.\nfunc updateUserInfo(id: Int64, name: String) {\n   let privateContext = DBStack.makeChildContext()\n   privateContext.perform {\n      let request: NSFetchRequest<UserInfoEntity> = NSFetchRequest(entityName: UserInfoEntity.entityName)\n      request.predicate = NSPredicate(format: "%K == %@", argumentArray: [#keyPath(UserInfoEntity.id), id])\n      request.fetchLimit = 1\n      do {\n         if let userInfo = try privateContext.fetch(request).first {\n            userInfo.name = name\n         } else {\n            _ = try UserInfoEntity(id: id, name: name, in: privateContext)\n         }\n         if privateContext.hasChanges {\n            print("\xe2\x86\x92 Will save userInfo. Name: " + name)\n            try privateContext.save()\n         }\n      } catch {\n         print(error)\n      }\n   }\n}\n\nlet stack = DBStack()\ntry stack.setupInMemoryStore()\nlet userID: Int64 = 1\nlet request: NSFetchRequest<UserInfoEntity> = NSFetchRequest(entityName: UserInfoEntity.entityName)\nrequest.predicate = NSPredicate(format: "%K == %@", argumentArray: [#keyPath(UserInfoEntity.id), userID])\nrequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(UserInfoEntity.name), ascending: false)]\nlet delegate = FetchedResultsDelegate()\nlet fetchedResultsController: NSFetchedResultsController<UserInfoEntity>\n   = NSFetchedResultsController(fetchRequest: request, managedObjectContext: DBStack.mainContext,\n                                sectionNameKeyPath: nil, cacheName: nil)\nfetchedResultsController.delegate = delegate\n\n// Here is our event handler. Called on main thread.\ndelegate.entityChanged = { [weak fetchedResultsController] in\n   let userInfo = fetchedResultsController?.fetchedObjects?.first\n   print("! UserInfo changed: \\(String(describing: userInfo?.name))")\n   // Update UI.\n}\n\ntry fetchedResultsController.performFetch()\n\nDispatchQueue.global().async {\n   updateUserInfo(id: userID, name: "Alex")\n   updateUserInfo(id: userID, name: "Alexander")\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

将打印:

\n\n
\xe2\x86\x92 Will save userInfo. Name: Alex\n! UserInfo changed: Optional("Alex")\n\xe2\x86\x92 Will save userInfo. Name: Alexander\n! UserInfo changed: Optional("Alexander")\n
Run Code Online (Sandbox Code Playgroud)\n

Tom*_*ton 5

一种方法是:

  1. objectID保存要监视的托管对象的属性值,而不是对托管对象的引用。
  2. 用于NotificationCenter添加通知的观察者NSManagedObjectContextObjectsDidChange,该通知由托管对象上下文生成。
  3. 当您收到此通知时,请在userInfo字典中查找名为 的键NSUpdatedObjectsKey。它包含对任何已更改的托管对象的引用。查看其中是否有objectID您在步骤 1 中保存的内容。

根据您希望事情如何运作,您可能NSManagedObjectContextDidSave更喜欢使用通知。您可能还想使用NSInsertedObjectsKey和/或NSDeletedObjectsKey