也许你可以提供帮助,我在其他问题上找不到类似的东西,所以我想我可能会错过一些明显的东西。
我在Swift中拥有CoreData + iCloud应用程序。
问题情景:
如果我已经在我的设备上登录iCloud,那么这样可以正常工作。
如果我退出iCloud,那么所有应用程序都可以正常工作,只要我执行问题场景,然后在第5步,NSManagedObject的managedObjectContext为nil,因此我无法对其进行任何更改,并且因为丢失了上下文一旦我需要现有对象的上下文,它就会崩溃。
我的问题:
我的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]
}
}
可能有用的其他信息:
@ 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当前令牌(null)
@ 2016-04-12 11:30:37:CoreDataStack:persistentStoreCoordinatorWillChangeStores:203:NSConcreteNotification 0x7fd3e8d8fdc0 {name = NSPersistentStoreCoordinatorStoresWillChangeNotification; object =; userInfo = { NSPersistentStoreUbiquitousTransitionTypeKey = 2; 补充=( " (URL:file:/// Users / maris / Library / Developer / CoreSimulator / Devices / F9A852DA-595C-4DE2-ADD7-7DECD7D814AD / data / Containers / Data / Application / 107B6DB1-C4DC-4626-8933-DACD0575F184 / Library /应用%20Support / iCloud.com.myCompany.myAppID / CoreDataUbiquitySupport /无人〜sim7CC36E42-82CB-5152-91BE-4DD26FE0A420 / DB /本地/存储/ DB.sqlite)" ); 删除=( " (URL:file:/// Users / maris / Library / Developer / CoreSimulator / Devices / F9A852DA-595C-4DE2-ADD7-7DECD7D814AD / data / Containers / Data / Application / 107B6DB1-C4DC-4626-8933-DACD0575F184 / Library /应用%20Support / iCloud.com.myCompany.myAppID / CoreDataUbiquitySupport /无人〜sim7CC36E42-82CB-5152-91BE-4DD26FE0A420 / DB /本地/存储/ DB.sqlite)" ); }} (主题):{number = 12,name = main}
@ 2016-04-12 11:30:37:AppDelegate:applicationDidBecomeActive:152:context: modelName:DBstoreURL: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/DB.sqlite 持久商店: * file:/// 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 /无人〜sim7CC36E42-82CB-5152-91BE-4DD26FE0A420 / DB /本地/存储/ DB.sqlite
stack.updateContextWithUbiquitousContentUpdates = true
但如果我没有将其设置为true,我想我不会立即获得iCloud的更新。答案 0 :(得分:0)
当您退出并重新启动时,应用程序将从内存中删除,因此会再次创建持久性存储和上下文,并将它们链接在一起。
当你投入背景然后带来没有发生的前锋时,商店和上下文都仍然存在。这里看起来正在发生的是持久性存储文件被更改并且上下文与它断开连接(这是一个猜测,我还没有对此进行测试)。
所以,看起来在persistentStoreCoordinatorWillChangeStores
中你应该真正破坏上下文并创建一个新的上下文。这也意味着销毁来自旧上下文的所有托管对象,并从新上下文中获取新版本(假设它们仍然存在)。
答案 1 :(得分:0)
好的,我找到了解决方案 - 也许这也有助于其他人。
基本上,通过检查iCloud是否已启用,然后定制CoreDataStack设置。
像这样:
init(inMemory:Bool = false)
{
self.inMemory = inMemory
self.options = [NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true]
if iCloudEnabled
{
self.options?[NSPersistentStoreUbiquitousContentNameKey] = CoreDataStack.storeName
self.monitorUbiquitousContentUpdatesIfiCloudEnabled()
}
}
更多细节可以在GitHub项目示例中找到 - 提交的差异解决了我的问题: https://github.com/marisveide/iCloudCoreDataProblem/commit/1d2da6c000bee7a66274192da2a637ba7c8cabf5#diff-8efc4cba62b5f1efad252e6f27b5b30b
整个项目可在此处下载: https://github.com/marisveide/iCloudCoreDataProblem
哇...那是"有趣"最近3天的调试和搜索。