关于核心数据和iCloud的WWDC 2013 207 session:
您在应用程序的本地内部提供单个商店URL 沙盒然后我们创建一个不透明的容器,里面有一个条目 它适用于系统上的每个帐户,包括本地帐户 是我们的术语,指的是当没有iCloud帐户时会发生什么 系统。这是一个由Core Data管理的特殊商店 你不必做任何特别的事情,因为你的用户没有 一个iCloud帐户。
在iOS 7 / OS X 10.9中,带有iCloud的Core Data会自动使用本地帐户来处理iCloud关闭的情况。与后备存储(在iCloud开启但无法访问时使用)不同,当服务开启时,本地帐户将完全被iCloud帐户替换,而不进行任何合并。只有iCloud关闭时,才能访问本地帐户中的数据。这种情况发生在:
以上是我从实验中理解的内容。如果我错了,请纠正我。
,本地帐户用户体验非常糟糕。如果您在关闭iCloud的情况下向应用添加数据然后将其打开,数据将“消失”,您可能会认为它已被删除。如果您在启用了iCloud的应用中添加数据,然后将其关闭,则数据也会“消失”。
我见过一些例子,试图通过向应用添加(更多)iCloud设置并管理他们自己的“本地”商店(不是iCloud提供的商店)来解决这个问题。这让我重复工作。
这种做法怎么样?
这类似于提醒所做的。但是,提醒会直接从iCloud设置向用户询问数据迁移,这是我们开发人员无法做到的事情。
1)这种方法是否有任何缺点或边界情况乍一看可能不明显?也许我们不打算像这样使用iCloud生成的本地帐户。
2)NSPersistentStoreCoordinatorStoresWillChangeNotification
和NSPersistentStoreCoordinatorStoresDidChangeNotification
是否足以检测到iCloud过渡的所有可能开启和关闭?
3)您是否会在NSPersistentStoreCoordinatorStoresWillChangeNotification
和NSPersistentStoreCoordinatorStoresDidChangeNotification
之间进行用户提示和合并,或者收集其中的所有信息并等到商店更改?我问,因为这些通知似乎是在后台发送的,阻止它们执行可能很长的操作可能不是Core Data所期望的。
答案 0 :(得分:3)
我将尝试回答我自己的问题,部分是为了组织我的想法,部分是为了回复@DuncanGroenewald。
1)这种方法是否有任何缺点或边界情况 乍一看不明显?
是。本地和iCloud帐户存储由Core Data管理,可以随时删除。
在实践中,我认为本地帐户商店不会被删除,因为它无法从iCloud重新创建。
关于iCloud帐户存储,我可以看到两种可能被删除的情况:a)在用户关闭iCloud后释放空间或b)因为用户通过选择设置>来请求它。 iCloud>全部删除。
如果用户请求了,那么您可能会认为数据迁移不是问题。
如果是为了腾出空间,那么是的,这是一个问题。但是,任何其他方法都存在同样的问题,因为当删除iCloud帐户存储时,您的应用程序未被唤醒。
2)NSPersistentStoreCoordinatorStoresWillChangeNotification和 NSPersistentStoreCoordinatorStoresDidChangeNotification足以 检测所有可能的开启和关闭到iCloud过渡?
是。无论iCloud是打开还是关闭,它都要求您始终使用NSPersistentStoreUbiquitousContentNameKey
创建持久性存储。像这样:
[self.managedObjectContext.persistentStoreCoordinator
addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:@{ NSPersistentStoreUbiquitousContentNameKey : @"someName" }
error:&error];
事实上,只听NSPersistentStoreCoordinatorStoresDidChangeNotification
就足够了(如下所示)。在启动时添加存储或在执行期间更改存储时将调用此方法。
3)你会做用户提示和合并吗? NSPersistentStoreCoordinatorStoresWillChangeNotification和 NSPersistentStoreCoordinatorStoresDidChangeNotification,或全部收集 那些信息,等到商店改变了?我问 因为这些通知似乎是在后台发送的,并且 阻止他们执行可能很长的操作可能不是 Core Data期望的是什么。
这就是我在NSPersistentStoreCoordinatorStoresDidChangeNotification
中的表现。
由于此通知在启动时和商店在执行期间发生更改时都会发送,因此我们可以使用它来保存当前商店网址和普遍存在的身份令牌(如果有)。
然后我们检查我们是否处于开/关转换场景并相应地迁移数据。
为了简洁起见,我没有包含任何UI代码,用户提示或错误处理。在进行任何迁移之前,您应该询问(或至少通知)用户。
- (void)storesDidChange:(NSNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
NSPersistentStoreUbiquitousTransitionType transitionType = [[userInfo objectForKey:NSPersistentStoreUbiquitousTransitionTypeKey] integerValue];
NSPersistentStore *persistentStore = [userInfo[NSAddedPersistentStoresKey] firstObject];
id<NSCoding> ubiquityIdentityToken = [NSFileManager defaultManager].ubiquityIdentityToken;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (transitionType != NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted) { // We only care of cases if the store was added or removed
NSData *previousArchivedUbiquityIdentityToken = [defaults objectForKey:HPDefaultsUbiquityIdentityTokenKey];
if (previousArchivedUbiquityIdentityToken) { // Was using ubiquity store
if (!ubiquityIdentityToken) { // Changed to local account
NSString *urlString = [defaults objectForKey:HPDefaultsPersistentStoreURLKey];
NSURL *previousPersistentStoreURL = [NSURL URLWithString:urlString];
[self importPersistentStoreAtURL:previousPersistentStoreURL
isLocal:NO
intoPersistentStore:persistentStore];
}
} else { // Was using local account
if (ubiquityIdentityToken) { // Changed to ubiquity store
NSString *urlString = [defaults objectForKey:HPDefaultsPersistentStoreURLKey];
NSURL *previousPersistentStoreURL = [NSURL URLWithString:urlString];
[self importPersistentStoreAtURL:previousPersistentStoreURL
isLocal:YES
intoPersistentStore:persistentStore];
}
}
}
if (ubiquityIdentityToken) {
NSData *archivedUbiquityIdentityToken = [NSKeyedArchiver archivedDataWithRootObject:ubiquityIdentityToken];
[defaults setObject:archivedUbiquityIdentityToken forKey:HPModelManagerUbiquityIdentityTokenKey];
} else {
[defaults removeObjectForKey:HPModelManagerUbiquityIdentityTokenKey];
}
NSString *urlString = persistentStore.URL.absoluteString;
[defaults setObject:urlString forKey:HPDefaultsPersistentStoreURLKey];
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI
});
}
然后:
- (void)importPersistentStoreAtURL:(NSURL*)importPersistentStoreURL
isLocal:(BOOL)isLocal
intoPersistentStore:(NSPersistentStore*)persistentStore
{
if (!isLocal) {
// Create a copy because we can't add an ubiquity store as a local store without removing the ubiquitous metadata first,
// and we don't want to modify the original ubiquity store.
importPersistentStoreURL = [self copyPersistentStoreAtURL:importPersistentStoreURL];
}
if (!importPersistentStoreURL) return;
// You might want to use a different concurrency type, depending on how you handle migration and the concurrency type of your current context
NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
importContext.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
NSPersistentStore *importStore = [importContext.persistentStoreCoordinator
addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:importPersistentStoreURL
options:@{NSPersistentStoreRemoveUbiquitousMetadataOption : @(YES)}
error:nil];
[self importContext:importContext intoContext:_managedObjectContext];
if (!isLocal) {
[[NSFileManager defaultManager] removeItemAtURL:importPersistentStoreURL error:nil];
}
}
数据迁移在importContext:intoContext
中执行。此逻辑将取决于您的模型以及重复和冲突策略。
我无法判断这是否会产生不良副作用。显然,这可能需要一段时间,具体取决于持久性存储的大小和数据。如果我发现任何问题,我会编辑答案。