2011-10-14 22 views
3

我有一個非常嚴重的問題。該應用程序是實時的,但不幸的是它在iOS 5上失敗了,我需要發佈更新。從整數16到整數32的核心數據更改屬性

的事情是很少的實體的ID列是整數16,但我需要改變,以整數32.

這顯然是我的錯誤,模型的創建很長一段時間以前,這是隻被重用。令我驚訝的是(現在)在iOS 4上,Core Data中的Integer 16可以很容易地將數字保持在500 000(錯誤?)的大小,但現在不能這樣工作 - 它給了我無效的數字。

應用程序是實時的,它有成功和核心數據也用於保持用戶的得分,成就等等,我不想刪除,迫使他們重新安裝應用程序。簡單地將不同實體中的十個屬性從Integer 16更改爲Integer 32最好的方法是什麼?

當然,我知道這些屬性的名稱和實體。

如果我只是在xcdatamodeld文件中更改這些屬性的Type列,它將適用於新用戶,但現有用戶的情況如何呢?他們的Documents文件夾中已經有sqlite文件。我相信我需要以某種方式改變持久性商店協調員。

還有什麼關於性能的事情,大約有10個屬性,新聞從16更改爲32,但核心數據通常情況下有超過100 000個內部對象。

關於你NSPersistentStore

回答

9

打開NSMigratePersistentStoresAutomaticallyOption' and 'NSInferMappingModelAutomaticallyOption,然後創建與修改模型的第二個版本。只做整數更改以保持簡單的遷移。這將允許安裝升級的用戶從已損壞的模型遷移到已更正的模型。

注意:必須是一個自動遷移;使用映射模型的手動遷移將不起作用。

+0

不幸的是,這是行不通的。自動遷移不會修復現有的負整數值。 –

+0

會發生什麼?從Int16到Int32仍然允許負數。你能舉一個例子嗎? –

+0

自動遷移確實修復了新的數據庫字段。但是,已存儲的值也需要進行遷移。快速解決方法是在從db讀取它們之後修復這些值:NSInteger myInt32 = databaseInt16 + 65536; –

0

只想確認Marcus S. Zarra的答案並加上一小部分。它在某種程度上對我們有好處。我們在模型中做出了完全相同的錯誤。但它有一個問題。在自動遷移過程中,似乎爲2^24的值將轉換爲16位值,但保存爲32位但值錯誤。

例如: 17 479 261變爲18 851

(17 479 261模(2^16)) - (2^16)= -18 851

我們下載的DB從電話並查看數據庫,並在數據庫中更改編號。

我們還沒有解決這個問題。

11

背景
以前版本的應用程序集屬性爲16位核心數據。 這太小了,無法保存大於32768的大值。
int 16使用1位來表示符號,所以最大值= 2^15 = 32768
在iOS 5中,這些值溢出爲負數。

34318成爲-31218
36745成爲-28791

修復這些負值,加2^16 = 65536
注意此解決方案僅如果原始值小於65536

添加一個新的模式
在文件瀏覽器中,選擇MyApp.xcdatamodeld
選擇菜單編輯/添加模型版本
版本名稱:提出「MyApp 2」,但您可以更改到MyAppVersion2
基於模型:MyApp的

在新MyAppVersion2.xcdatamodel改變屬性從整數16類型整數64

在文件瀏覽,選擇目錄MyApp.xcdatamodeld

打開右邊的窗格中檢查,版本化的核心數據模型當前從MyApp更改爲MyAppVersion2。 在左窗格文件導航器中,綠色複選標記從MyApp.xcdatamodel移動到MyAppVersion2.xcdatamodel。

在MyAppAppDelegate managedObjectModel不改變從資源名@ 「MyApp的」

在Xcode中選擇文件夾ModelClasses。
文件/添加核心數據映射模型。

選擇源數據模型MyApp.xcdatamodel
選擇目標數據模型MyAppVersion2.xcdatamodel
另存爲MyAppToMyAppVersion2.xcmappingmodel

添加到目標MyApp的。

在上CoreData手動遷移MyAppAppDelegate persistentStoreCoordinator轉

// Returns the persistent store coordinator for the application. 
// If the coordinator doesn't already exist, it is created 
// and the application's store added to it. 
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { 

    if (persistentStoreCoordinator_ != nil) { 
     return persistentStoreCoordinator_; 
    } 

    NSURL *storeURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] 
               stringByAppendingPathComponent: @"MyApp.sqlite"]]; 

    NSError *error = nil; 
    persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] 
            initWithManagedObjectModel:[self managedObjectModel]]; 

    // Set Core Data migration options 
    // For automatic lightweight migration set NSInferMappingModelAutomaticallyOption to YES 
    // For manual migration using a mapping model set NSInferMappingModelAutomaticallyOption to NO 
    NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], 
             NSMigratePersistentStoresAutomaticallyOption, 
             [NSNumber numberWithBool:NO], 
             NSInferMappingModelAutomaticallyOption, 
             nil]; 

    if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType 
                configuration:nil 
                  URL:storeURL 
                 options:optionsDictionary 
                  error:&error]) 
    { 
     // handle the error 
     NSString *message = [[NSString alloc] 
          initWithFormat:@"%@, %@", error, [error userInfo]]; 

     UIAlertViewAutoDismiss *alertView = [[UIAlertViewAutoDismiss alloc]  
              initWithTitle:NSLocalizedString(@"Sorry, Persistent Store Error. Please Quit.", @"") 
              message:message 
              delegate: nil 
              cancelButtonTitle:NSLocalizedString(@"OK", @"") 
              otherButtonTitles:nil]; 
     [message release]; 
     [alertView show]; 
     [alertView release]; 
    }  

    return persistentStoreCoordinator_; 
} 

添加遷移策略
MyAppToMyAppVersion2MigrationPolicy
以下示例一個實體 「環境」 轉換與一個整數屬性 「FeedID」 和字符串屬性「標題」。

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)aSource 
            entityMapping:(NSEntityMapping *)mapping 
             manager:(NSMigrationManager *)migrationManager 
              error:(NSError **)error { 
    NSEntityDescription *aSourceEntityDescription = [aSource entity]; 
    NSString *aSourceName = [aSourceEntityDescription valueForKey:@"name"]; 

    NSManagedObjectContext *destinationMOC = [migrationManager destinationContext]; 
    NSManagedObject *destEnvironment; 
    NSString *destEntityName = [mapping destinationEntityName]; 

    if ([aSourceName isEqualToString:kEnvironment]) 
    { 
     destEnvironment = [NSEntityDescription 
          insertNewObjectForEntityForName:destEntityName 
          inManagedObjectContext:destinationMOC]; 

     // attribute feedID 
     NSNumber *sourceFeedID = [aSource valueForKey:kFeedID]; 
     if (!sourceFeedID) 
     { 
      // Defensive programming. 
      // In the source model version, feedID was required to have a value 
      // so excecution should never get here. 
      [destEnvironment setValue:[NSNumber numberWithInteger:0] forKey:kFeedID]; 
     } 
     else 
     { 
      NSInteger sourceFeedIDInteger = [sourceFeedID intValue];   
      if (sourceFeedIDInteger < 0) 
      {    
       // To correct previous negative feedIDs, add 2^16 = 65536    
       NSInteger kInt16RolloverOffset = 65536;    
       NSInteger destFeedIDInteger = (sourceFeedIDInteger + kInt16RolloverOffset); 
       NSNumber *destFeedID = [NSNumber numberWithInteger:destFeedIDInteger]; 
       [destEnvironment setValue:destFeedID forKey:kFeedID]; 

      } else 
      { 
       // attribute feedID previous value is not negative so use it as is 
       [destEnvironment setValue:sourceFeedID forKey:kFeedID]; 
      } 
     } 

     // attribute title (don't change this attribute) 
     NSString *sourceTitle = [aSource valueForKey:kTitle]; 
     if (!sourceTitle) 
     { 
      // no previous value, set blank 
      [destEnvironment setValue:@"" forKey:kTitle]; 
     } else 
     { 
      [destEnvironment setValue:sourceTitle forKey:kTitle]; 
     } 

     [migrationManager associateSourceInstance:aSource 
          withDestinationInstance:destEnvironment 
           forEntityMapping:mapping]; 

     return YES; 
    } else 
    { 
     // don't remap any other entities 
     return NO; 
    } 
} 

在文件導航器中選擇MyAppToMyAppVersion2.xcmappingmodel
在窗口,顯示右側窗格中的實用程序。
在窗口中,選擇實體映射EnvironmentToEnvironment
在右側的Entity Mapping中,選擇Custom Policy,然後輸入MyAppToMyAppVersion2MigrationPolicy。
保存文件。

參考文獻:

Zarra,核心數據第5章第87頁http://pragprog.com/book/mzcd/core-data

http://www.informit.com/articles/article.aspx?p=1178181&seqNum=7

http://www.timisted.net/blog/archive/core-data-migration/

http://www.cocoabuilder.com/archive/cocoa/286529-core-data-versioning-non-trivial-value-expressions.html

http://www.seattle-ipa.org/2011/09/11/coredata-and-integer-width-in-ios-5/

Privat,iOS的專業核心數據Ch 8 p273