也许您可以提供帮助,在其他问题上我找不到类似的东西,所以我想我可能会错过一些显而易见的事情。

我在Swift中有CoreData + iCloud App。

问题场景:

  • 启动应用程序
  • NSManagedObject读取/更新
  • 按“主页”(使应用程序处于非 Activity 状态)
  • 恢复应用程序
  • NSManagedObject读取/更新

  • 如果我已在设备上登录iCloud,则可以正常工作。

    如果我退出iCloud,则所有应用程序都可以正常运行,即使我遇到问题,也可以在第5步中将NSManagedObject的managedObjectContext设为nil,因此我无法对其进行任何更改,并且由于上下文丢失而无法使用我需要现有对象的上下文时发生崩溃。

    我的问题:
  • 为什么在“问题方案”中会发生这种情况?
  • 如何解决此问题,以便在没有iCloud用户登录的情况下,如果应用程序处于非 Activity 状态然后又处于 Activity 状态,则CoreData可以继续工作吗?

  • 我的CoreDataStack:
    class CoreDataStack: CustomStringConvertible
    {
        static let sharedManager = CoreDataStack()
        static let applicationDocumentsDirectoryName = "iCloud.com.myCompany.myAppID"
        static let errorDomain = "CoreDataStack"
    
        static let modelName = "DB"
        static let storeName = "DB"
        static var storeFileName: String
        {
            return storeName + ".sqlite"
        }
        var options : [String : AnyObject]?
    
        var inMemory: Bool = false
    
    
        var description: String
        {
            var desc = "context: \(self.managedObjectContext)\n" +
                "modelName: \(CoreDataStack.modelName)" +
                "storeURL: \(self.storeURL)"
    
            desc += "\nPersistent Stores:\n"
            for store in persistentStoreCoordinator.persistentStores
            {
                desc += "* \(store.URL!.absoluteString)"
            }
    
            return desc
        }
    
    
        lazy var managedObjectModel: NSManagedObjectModel =
        {
            let modelURL = NSBundle.mainBundle().URLForResource(modelName, withExtension: "momd")!
            return NSManagedObjectModel(contentsOfURL: modelURL)!
        }()
    
    
        lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator =
        {
            let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
    
            do
            {
                if self.inMemory
                {
                    try coordinator.addPersistentStoreWithType(
                        NSInMemoryStoreType,
                        configuration: nil,
                        URL: nil,
                        options: nil)
                } else
                {
                    try coordinator.addPersistentStoreWithType(
                        NSSQLiteStoreType,
                        configuration: nil,
                        URL: self.storeURL,
                        options: self.options)
                }
            } catch var error as NSError
            {
                VTLog.error("Persistent Store Error: \(error)")
            } catch
            {
                fatalError("Error creating Persistent Store!")
            }
            return coordinator
        }()
    
    
        /// The directory the application uses to store the Core Data store file.
        lazy var applicationSupportDirectory: NSURL =
        {
            let fileManager = NSFileManager.defaultManager()
            let urls = fileManager.URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask)
            let applicationSupportDirectoryURL = urls.last!
            let applicationSupportDirectory =
                applicationSupportDirectoryURL.URLByAppendingPathComponent(applicationDocumentsDirectoryName)
    
            do
            {
                let properties = try applicationSupportDirectory.resourceValuesForKeys([NSURLIsDirectoryKey])
    
                if let isDirectory = properties[NSURLIsDirectoryKey] as? Bool where isDirectory == false
                {
                    let description = NSLocalizedString("Could not access the application data folder.",
                                                        comment: "Failed to initialize applicationSupportDirectory.")
                    let reason = NSLocalizedString("Found a file in its place.",
                                                   comment: "Failed to initialize applicationSupportDirectory.")
    
                    throw NSError(domain: errorDomain, code: 201, userInfo:
                    [
                        NSLocalizedDescriptionKey: description,
                        NSLocalizedFailureReasonErrorKey: reason
                    ])
                }
            } catch let error as NSError where error.code != NSFileReadNoSuchFileError
            {
                fatalError("Error occured: \(error).")
            } catch
            {
                let path = applicationSupportDirectory.path!
    
                do
                {
                    try fileManager.createDirectoryAtPath(path, withIntermediateDirectories:true, attributes:nil)
                }
                catch
                {
                    fatalError("Could not create application documents directory at \(path).")
                }
            }
    
            return applicationSupportDirectory
        }()
    
    
        /// URL for the main Core Data store file.
        lazy var storeURL: NSURL =
        {
            return self.applicationSupportDirectory.URLByAppendingPathComponent(storeFileName)
        }()
    
    
        lazy var managedObjectContext: NSManagedObjectContext =
        {
            let context = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
            context.persistentStoreCoordinator = self.persistentStoreCoordinator
            return context
        }()
    
    
        // ****************************************
        // MARK: - iCloud Sync
        // ****************************************
    
        var updateContextWithUbiquitousContentUpdates: Bool = false
        {
            willSet
            {
                ubiquitousChangesObserver = newValue ? NSNotificationCenter.defaultCenter() : nil
            }
        }
    
    
        private var ubiquitousChangesObserver: NSNotificationCenter?
        {
            didSet
            {
                oldValue?.removeObserver(
                    self,
                    name: NSPersistentStoreDidImportUbiquitousContentChangesNotification,
                    object: persistentStoreCoordinator)
    
                ubiquitousChangesObserver?.addObserver(
                    self,
                    selector: #selector(self.persistentStoreDidImportUbiquitousContentChanges(_:)),
                    name: NSPersistentStoreDidImportUbiquitousContentChangesNotification,
                    object: persistentStoreCoordinator)
    
    
                oldValue?.removeObserver(
                    self,
                    name: NSPersistentStoreCoordinatorStoresWillChangeNotification,
                    object: persistentStoreCoordinator)
    
                ubiquitousChangesObserver?.addObserver(
                    self,
                    selector: #selector(self.persistentStoreCoordinatorWillChangeStores(_:)),
                    name: NSPersistentStoreCoordinatorStoresWillChangeNotification,
                    object: persistentStoreCoordinator)
            }
        }
    
    
        @objc func persistentStoreDidImportUbiquitousContentChanges(notification: NSNotification)
        {
            VTLog.debug("Merging ubiquitous content changes")
            VTLog.debug(notification)
    
            self.managedObjectContext.performBlock
            {
                self.managedObjectContext.mergeChangesFromContextDidSaveNotification(notification)
            }
        }
    
    
        @objc func persistentStoreCoordinatorWillChangeStores(notification: NSNotification)
        {
            VTLog.debug(notification)
    
            if managedObjectContext.hasChanges
            {
                do
                {
                    try managedObjectContext.save()
                } catch let error as NSError
                {
                    print("Error saving: \(error)", terminator: "")
                }
            }
            managedObjectContext.reset()
        }
    
    
        // ***********************************************
        // * Data: iCloud Container Actions
        // ***********************************************
    
        func deleteiCloudContainer()
        {
            VTLog.debug("Deleting iCloud Container...")
    
            let currentStore = managedObjectContext.persistentStoreCoordinator!.persistentStores.last!
    
            VTLog.debug("Located data store [\(currentStore)]")
    
            managedObjectContext.reset()
            VTLog.debug("managedObjectContext.reset() - OK")
    
            do
            {
                try managedObjectContext.persistentStoreCoordinator?.removePersistentStore(currentStore)
                VTLog.debug("removePersistentStore() - OK")
            } catch let error as NSError
            {
                VTLog.error("Could not remove persistent store [\(currentStore)]: \(error)")
            }
    
            do
            {
                try NSPersistentStoreCoordinator.removeUbiquitousContentAndPersistentStoreAtURL(
                    currentStore.URL!, options: currentStore.options)
                VTLog.debug("removeUbiquitousContentAndPersistentStoreAtURL() - OK")
            } catch let error as NSError
            {
                VTLog.error("Could not remove Ubiquitous Content and Persistent Store at URL [\(currentStore)]: \(error)")
            }
        }
    
    
        //*******************************************
        // MARK: - Init
        //*******************************************
    
        init(inMemory:Bool = false)
        {
            self.inMemory = inMemory
    
            self.options = [NSMigratePersistentStoresAutomaticallyOption: true,
                NSInferMappingModelAutomaticallyOption: true,
                NSPersistentStoreUbiquitousContentNameKey: CoreDataStack.storeName]
        }
    
    }
    

    可能有帮助的其他信息:
  • 所有版本均在Simulator上,而不论版本:iOS 9.2,iOS 9.3。
  • 登录到iCloud后,一切正常。
  • 我注意到DB.sqlite文件实际上不存在于路径storeURL上,它是在路径上创建的,如下所示,但是无论是否使用iCloud登录,它都是相同的,因此我不知道是否应该这样。
  • 还原应用程序时,我看到以下操作序列:

  • @ 2016-04-12 11:30:36:AppDelegate:applicationDidEnterBackground:133:
    (线程):{number = 10,name = main}

    @ 2016-04-12 11:30:37:AppDelegate:applicationWillEnterForeground:141:
    (线程):{number = 11,name = main}

    2016-04-12 11:30:37.150数数我自己[57886:19968276] -PFUbiquitySwitchboardEntryMetadata setUseLocalStorage::CoreData:Ubiquity:nobody〜sim7CC36E42-82CB-5152-91BE-4DD26FE0A420:DB
    使用本地存储:1用于新的NSFileManager当前 token (空)

    @ 2016-04-12 11:30:37:CoreDataStack:persistentStoreCoordinatorWillChangeStores:203:NSConcreteNotification 0x7fd3e8d8fdc0 {name = NSPersistentStoreCoordinatorStoresWillChangeNotification;对象=; userInfo = {
    NSPersistentStoreUbiquitousTransitionTypeKey = 2;
    添加=(
    ”(网址:file:/// Users / maris / Library / Developer / CoreSimulator / Devices / F9A852DA-595C-4DE2-ADD7-7DECD7D814AD / data / Containers / Data / Application / 107B6DB1-C4DC-4626-8933-DACD0575F184 / Library /Application%20Support/iCloud.com.myCompany.myAppID/CoreDataUbiquitySupport/nobody~sim7CC36E42-82CB-5152-91BE-4DD26FE0A420/DB/local/store/DB.sqlite)“
    );
    删除=(
    ”(网址:file:/// Users / maris / Library / Developer / CoreSimulator / Devices / F9A852DA-595C-4DE2-ADD7-7DECD7D814AD / data / Containers / Data / Application / 107B6DB1-C4DC-4626-8933-DACD0575F184 / Library /Application%20Support/iCloud.com.myCompany.myAppID/CoreDataUbiquitySupport/nobody~sim7CC36E42-82CB-5152-91BE-4DD26FE0A420/DB/local/store/DB.sqlite)“
    );
    }}
    (线程):{number = 12,name = main}

    @ 2016-04-12 11:30:37:AppDelegate:applicationDidBecomeActive:152:上下文:
    modelName:DBstoreURL:文件:///用户/ maris / Library / Developer / CoreSimulator / Devices / F9A852DA-595C-4DE2-ADD7-7DECD7D814AD / data / Containers / Data / Application / 107B6DB1-C4DC-4626-8933-DACD0575F184 / Library /Application%20Support/iCloud.com.myCompany.myAppID/DB.sqlite
    永久商店:
    *文件:/// Users / maris / Library / Developer / CoreSimulator / Devices / F9A852DA-595C-4DE2-ADD7-7DECD7D814AD / data / Containers / Data / Application / 436959B5-7850-4156-AB3D-A11BE72FF1AF / Library / Application% 20Support / iCloud.com.myCompany.myAppID / CoreDataUbiquitySupport / nobody〜sim7CC36E42-82CB-5152-91BE-4DD26FE0A420 / DB / local / store / DB.sqlite
  • 设置此问题时出现:stack.updateContextWithUbiquitousContentUpdates = true但是,如果我不将其设置为true,我想我将不会立即从iCloud获得更新。
  • 在GitHub上看到演示该问题的Xcode项目:https://github.com/marisveide/iCloudCoreDataProblem(请阅读顶部的AppDelegate.swift文件注释)
    看到代码在单独的分支中更改,而解决方案又不会丢失Context,那真是太棒了。
  • 最佳答案

    退出并重新启动时,该应用程序将从内存中删除,因此将再次创建持久性存储和上下文并将它们链接在一起。

    当您放到背景中,然后再提出没有发生的事情时,存储和上下文都仍然存在。此处似乎正在发生的事情是更改了持久性存储文件,并且上下文与该文件断开了连接(这有点猜测,我还没有对此进行测试)。

    因此,看起来在persistentStoreCoordinatorWillChangeStores中,您应该真正销毁上下文并创建一个新的上下文。这也意味着销毁所有来自旧上下文的托管对象,并从新上下文中获取新版本(假设它们仍然存在)。

    关于ios - 从非事件状态恢复应用程序后,NSManagedObject的managedObjectContext变为零,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36567966/

    10-14 21:03
    查看更多