1

我正在开发一个仅限 iOS 7 的应用程序,而且我对 iCloud 和 Core 数据还很陌生。我的假设是,如果可能,用户总是希望使用 iCloud 进行备份。这就是为什么我按如下方式设置我的持久性存储:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

 if (__persistentStoreCoordinator != nil) {

      return __persistentStoreCoordinator;

 }

 NSURL *storeURL = [[[self applicationDocumentsDirectory]

                                URLByAppendingPathComponent:storeNameWithoutFileExtension isDirectory:NO]

                               URLByAppendingPathExtension:@"sqlite"];

 NSError *error = nil;

 __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];



 [[NSNotificationCenter defaultCenter] addObserver:self

                                                    selector:@selector(persistentStoreDidImportUbiquitiousContentChanges:)

                                                         name:NSPersistentStoreDidImportUbiquitousContentChangesNotification

                                                      object:__persistentStoreCoordinator];



 [[NSNotificationCenter defaultCenter] addObserver:self

                                                    selector:@selector(storesWillChange:)

                                                         name:NSPersistentStoreCoordinatorStoresWillChangeNotification

                                                      object:__persistentStoreCoordinator];



 [[NSNotificationCenter defaultCenter] addObserver:self

                                                    selector:@selector(storesDidChange:)

                                                         name:NSPersistentStoreCoordinatorStoresDidChangeNotification

                                                      object:__persistentStoreCoordinator];





 NSDictionary *options = @{ NSInferMappingModelAutomaticallyOption : @YES,

                                  NSPersistentStoreUbiquitousContentNameKey : storeNameWithoutFileExtension, };

// NSPersistentStoreRebuildFromUbiquitousContentOption

// NSMigratePersistentStoresAutomaticallyOption: @YES,

// NSMigratePersistentStoresAutomaticallyOption : @YES,



 if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType

                                                             configuration:nil

                                                                         URL:storeURL

                                                                    options:options

                                                                       error:&error])

 {

      NSLog(@"Unresolved error %@, %@", error, [error userInfo]);

      abort();

 }



 return __persistentStoreCoordinator;

}

通过此设置,即使用户未登录 iCloud(使用“本地”文件夹),我也总是立即开始使用 Documents/CoreDataUbiquitySupport 下的文件夹。

如您所见,我正在注册通知。

  • 为什么在 NSPersistentStoreCoordinatorStoresWillChangeNotification 和 NSPersistentStoreCoordinatorStoresDidChangeNotification 的 NSNotification 中的键 NSAddedPersistentStoresKey 和 NSRemovedPersistentStoresKey 的数组中添加和删除的持久性存储总是相同的,例如对于添加的案例帐户?人们会期望通知包含已删除的持久性存储和新添加的。所以他们不应该是一样的。但不幸的是,它们总是相同的。

当前的行为是,如果我切换帐户或注销帐户或登录到新帐户,应用程序总是从关联的商店开始,如果以前没有,则使用新的商店。我想要的行为是应用程序始终将数据迁移到新商店。例如:用户未登录 iCloud 并开始使用我的应用程序。他创建了保存在 Documents/CoreDataUbiquitySupport 中的本地数据。现在他打开了 iCloud。现在我希望将本地数据迁移到云端。目前这没有发生。我必须采用哪种方法来实现迁移,我该如何实现呢?我可以使用“migratePersistentStore”方法吗?我尝试在 NSPersistentStoreCoordinatorStoresDidChangeNotification 中使用以下代码,但无济于事:

 - (void)storesDidChange:(NSNotification *)n {

 // refresh user interface
 switch ([[n.userInfo objectForKey:NSPersistentStoreUbiquitousTransitionTypeKey] integerValue]) {

      case NSPersistentStoreUbiquitousTransitionTypeAccountAdded : {

           NSLog(@"NSPersistentStoreUbiquitousTransitionTypeAccountAdded migrate");


           NSError *error = nil;

           NSURL *storeURL = ((NSPersistentStore *)asdf[0]).URL;

           NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption : @YES,

                                            NSInferMappingModelAutomaticallyOption : @YES,

                                            NSPersistentStoreUbiquitousContentNameKey : storeNameWithoutFileExtension, };

           NSPersistentStore *asdfasdf = [__persistentStoreCoordinator migratePersistentStore:self.oldStore toURL:storeURL options:options withType:NSSQLiteStoreType error:&error];
      }

      break;

      case NSPersistentStoreUbiquitousTransitionTypeAccountRemoved :

           NSLog(@"NSPersistentStoreUbiquitousTransitionTypeAccountRemoved");



           break;

      case NSPersistentStoreUbiquitousTransitionTypeContentRemoved : {

           NSLog(@"NSPersistentStoreUbiquitousTransitionTypeContentRemoved migrate");

           NSError *error = nil;

           NSURL *storeURL = ((NSPersistentStore *)asdf[0]).URL;

           NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption : @YES,

                                            NSInferMappingModelAutomaticallyOption : @YES,

                                            NSPersistentStoreUbiquitousContentNameKey : storeNameWithoutFileExtension, };

           NSPersistentStore *asdfasdf = [__persistentStoreCoordinator migratePersistentStore:self.oldStore toURL:storeURL options:options withType:NSSQLiteStoreType error:&error];

      }

      break;

      case NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted :

           NSLog(@"NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted");

           NSLog(@"initial import");



           break;

      default :

           break;

 }

}

变量 oldStore 是在 NSPersistentStoreCoordinatorStoresWillChangeNotification 中设置的,因为我不能依赖 NSNotification 及其键 NSAddedPersistentStoresKey 和 NSRemovedPersistentStoresKey 因为如前所述,两个数组始终包含与前面描述的相同的存储。

  • 我将在哪种方法/什么时候实现重复数据删除代码?我可以在其中一个通知中执行此操作吗?

  • 对于哪个用例,我必须使用 NSMigratePersistentStoresAutomaticallyOption?该选项是如何工作的?

我在玩我的应用程序时看到的一个奇怪行为是,如果用户没有登录到 iCloud。当我将我的应用程序带到后台并退出 iCloud 并返回我的应用程序时,我得到的 NSNotifications 的类型是 NSPersistentStoreUbiquitousTransitionTypeContentRemoved。我希望 NSPersistentStoreUbiquitousTransitionTypeAccountRemoved。如果我随后将应用程序置于后台并再次备份,我将获得类型为 NSPersistentStoreUbiquitousTransitionTypeAccountRemoved 的 NSNotifications。但不幸的是,当用户未登录到 iCloud 并且应用程序正在运行并且用户将应用程序带到后台然后回到前台并且我得到类型为 NSPersistentStoreUbiquitousTransitionTypeAccountRemoved 的 NSNotifications 时,NSPersistentStoreCoordinatorStoresWillChangeNotification 和 NSPersistentStoreCoordinatorStoresDidChangeNotification 总是为用例触发。这总是发生。- 这是为什么?

为了使我在这里使用的代码完整,是我的 NSPersistentStoreCoordinatorStoresWillChangeNotification 和 NSPersistentStoreDidImportUbiquitousContentChangesNotification 的代码以及其他几个函数:

- (void)storesWillChange:(NSNotification *)n {

 NSLog(@"storesWillChange");

 NSArray *pStores = self.persistentStoreCoordinator.persistentStores;
 NSManagedObjectContext *moc = [self managedObjectContext];



 switch ([[n.userInfo objectForKey:NSPersistentStoreUbiquitousTransitionTypeKey] integerValue]) {

      case NSPersistentStoreUbiquitousTransitionTypeAccountAdded : {

           NSLog(@"NSPersistentStoreUbiquitousTransitionTypeAccountAdded");

           self.oldStore = pStores[0];

      }

      break;

      case NSPersistentStoreUbiquitousTransitionTypeAccountRemoved :

           NSLog(@"NSPersistentStoreUbiquitousTransitionTypeAccountRemoved");



           break;

      case NSPersistentStoreUbiquitousTransitionTypeContentRemoved :

           NSLog(@"NSPersistentStoreUbiquitousTransitionTypeContentRemoved");

           self.oldStore = pStores[0];

           break;

      case NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted :

           NSLog(@"NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted");

           NSLog(@"initial import");



           break;

      default :

           break;

 }

 [moc performBlockAndWait:^{

       NSError *error = nil;

       if ([moc hasChanges]) {

            [moc save:&error];

       }

       [moc reset];

  }];


 // reset user interface


 }




- (void)persistentStoreDidImportUbiquitiousContentChanges:(NSNotification    *)changeNotification {

 NSLog(@"*** Incoming iCloud Data ***");

 NSManagedObjectContext *moc = [self managedObjectContext];



 [moc performBlock:^{

       [moc mergeChangesFromContextDidSaveNotification:changeNotification];

  }];

 }




 - (NSManagedObjectContext *)managedObjectContext {

 if (__managedObjectContext != nil) {

      return __managedObjectContext;

 }



 NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];

 if (coordinator != nil) {

      __managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

 //          __managedObjectContext = [[NSManagedObjectContext alloc] init];

      [__managedObjectContext setPersistentStoreCoordinator:coordinator];

      [__managedObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];

 }

 return __managedObjectContext;

 }


 - (NSManagedObjectModel *)managedObjectModel {

 if (__managedObjectModel != nil) {

      return __managedObjectModel;

 }

 NSURL *modelURL = [[NSBundle mainBundle] URLForResource:dataModelName withExtension:@"momd"];

 __managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

 return __managedObjectModel;

 }

所以基本上我想要完成的是用户拥有相同的数据,无论他使用哪个 iCloud 帐户,即使它是“本地”的。

任何帮助,将不胜感激。先感谢您!

4

2 回答 2

2

要将数据从一个 Core Data 存储迁移到另一个(无论是否使用 iCloud) ,请migratePersistentStore:toURL:options:withType:error:使用NSPersistentStoreCoordinator.

但是,确保所有 iCloud 帐户之间的数据始终相同(已迁移)是:

  • 可能很困难(至少我不知道你会怎么做)。
  • 可能不是您的用户想要的。每个 iCloud 帐户都有自己的数据是有意义的。

从 iOS 7 开始,iCloud 核心数据得到了显着改进和极大地简化(针对开发人员)。简化之一是 Core Data 现在透明地处理 iCloud 帐户更改和本地后备存储,如果您打开一个无处不在的存储但用户没有登录到 iCloud,则创建一个“本地”存储。

不幸的是,这个“本地”商店被视为一个(虚拟)iCloud 帐户,因此从它迁移到另一个(真实)iCloud 帐户很棘手。因此,最好在您的应用程序中设置“使用 iCloud:是/否”选项。如果用户选择“否”,则创建一个真正的本地(未启用 iCloud)存储(存储在Documents文件夹中)。现在,当用户在您的应用程序中更改 iCloud 选项时,您可以选择将此本地存储迁移到启用了 iCloud 的存储(使用migratePersistentStore:toURL:options:withType:error:),反之亦然,如果用户从 iCloud 切换到本地。

你问的另一个问题是:

对于哪个用例,我必须使用 NSMigratePersistentStoresAutomaticallyOption?该选项是如何工作的?

选项NSMigratePersistentStoresAutomaticallyOptionNSInferMappingModelAutomaticallyOption用于将商店迁移到更新的数据模型(例如,如果您向实体添加字段),与从一个商店迁移到另一个商店无关。它们与您要在此处解决的问题无关。

于 2013-12-09T10:43:38.940 回答
-1

我建议不要在 iOS6 中使用 iCloud+CoreData。有很多错误。读这个:

http://www.theverge.com/2013/3/26/4148628/why-doesnt-icloud-just-work

http://www.zdnet.com/developers-give-apple-an-icloud-ultimatum-fix-it-by-june-7000013495/

编辑

在 iOS7 中似乎有所改进,但开发人员表示在更改模型版本时会出现问题。:(

于 2013-10-17T19:49:38.480 回答