我将尝试回答我自己的问题,部分是整理我的想法,部分是回复@DuncanGroenewald。
1) 这种方法是否有任何缺点或边界情况
乍一看不明显?
对本地和iCloud帐户存储由Core Data管理,可以随时删除。
实际上,我认为不会删除本地帐户存储,因为它无法从iCloud中重新创建。
关于iCloud帐户存储,我可以看到两种情况,它们可能会被删除:a)在用户关闭iCloud后释放空间,或b)因为用户通过选择
设置>iCloud>全部删除
.
如果用户请求,那么您可能会认为数据迁移不是问题。
如果要释放空间,那么是的,这是个问题。然而,在任何其他方法中都存在相同的问题,因为当删除iCloud帐户存储时,您的应用程序不会被唤醒。
2) NSPersistenStoreCoordinatorStoresWillChangeNotification和
NSPersistentStoreCoordinatorStoresDidChangeNotification足以
检测所有可能的从开到关和从关到开的iCloud转换?
对它要求您始终使用
NSPersistentStoreUbiquitousContentNameKey
,无论iCloud是否打开或关闭。如下所示:
[self.managedObjectContext.persistentStoreCoordinator
addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:@{ NSPersistentStoreUbiquitousContentNameKey : @"someName" }
error:&error];
事实上,只听
NSPersistentStoreCoordinatorStoresDidChangeNotification
足够了(如下所示)。当在启动时添加存储或在执行期间更改存储时,将调用此命令。
3) 您是否会执行用户提示并在
NSPersistenStoreCoordinatorStoresWillChangeNotification和
NSPersistentStoreCoordinatorStoresDidChangeNotification,或收集所有
然后等待商店更改?我问
因为这些通知似乎是在后台发送的,并且
阻止它们执行可能较长的操作可能不是
核心数据所期望的。
我会这样做
NSPersistentStoreCoordinatorStoresDidChangeNotification
.
由于此通知在启动时发送,并且在执行期间存储发生更改时发送,因此我们可以使用它来保存当前存储url和普遍存在的标识令牌(如果有的话)。
然后我们检查是否处于打开/关闭转换场景中,并相应地迁移数据。
为了简洁起见,我没有包括任何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
。此逻辑将取决于您的模型以及重复和冲突策略。
我不知道这是否会有不想要的副作用。显然,这可能需要相当长的时间,具体取决于持久存储的大小和数据。如果我发现任何问题,我会编辑答案。