从 NSDate 到 NSString 的转换,属性上的核心数据迁移未正确显示字符串

Posted

技术标签:

【中文标题】从 NSDate 到 NSString 的转换,属性上的核心数据迁移未正确显示字符串【英文标题】:Conversion from NSDate to NSString with Core-Data Migration on Attribute Not Displaying the String Appropriately 【发布时间】:2014-04-15 10:06:21 【问题描述】:

很抱歉这个问题太长了;如果不包含大量代码和图像,我不知道如何解释这一点。

我正在更新我的应用程序,因为我发现了与日期相关的问题,我不得不将属性从 NSDate 更改为 NSString。我正在为我的应用程序使用 Core Data。

应用的型号是:

交易实体

日期实体

年实体

Year 实体有一个名为 yearOfEvent 的属性,类型为 NSDate,并且随着对我的应用程序的更新(已经在应用商店中),我将 yearOfEvent 更改为 NSString

一些研究和对另一个问题的回答告诉我,我必须自己进行迁移。

这主要是有效的,但还没有完全实现。

我的应用程序是一个双标签UITabBarController,其中两个标签都是UITableViewControllers。第一个是按时间顺序排列的条目的“时间线”。第二个选项卡应该只显示已输入的年份;因此,如果用户有 2014、2013 和 2012 年的条目,您将在“日期”选项卡的单元格中看到这些条目,当您点击该单元格时,它会显示该年份的条目。这就是我想在这里改变的。该应用程序由用户向UITextFields 添加一些信息并从UIDatePicker 中选择一个日期来填充,该日期将保存到Core-Data,然后使用NSFetchedResultsController 更新表格。

问题

现在,当我让 App Store 中的应用程序运行数据,然后使用自定义迁移迁移到我的应用程序的新版本时,时间轴很好,但 Dates TableViewController 的单元格的标签显示为空白年。单元格的数量是正确的,但年份是空白的。

我在这个问题中遵循了公认的答案:Example or explanation of Core Data Migration with multiple passes? 与代码的第一位,但我不确定将第二部分(代码的最后部分)放在哪里。

所以在我的模型类中,我有:

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance
                                      entityMapping:(NSEntityMapping *)mapping
                                            manager:(NSMigrationManager *)manager
                                              error:(NSError **)error

    // Create a new object for the model context
    NSManagedObject *newObject =
    [NSEntityDescription insertNewObjectForEntityForName:[mapping destinationEntityName]
                                  inManagedObjectContext:[manager destinationContext]];

    // do our transfer of nsdate to nsstring
    NSDate *date = self.transaction.dates.dateOfEvent;
    //NSDate *date = [sInstance valueForKey:@"timeStamp"];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

    [dateFormatter setDateFormat:@"y"];
    //NSString *stringFromYear = [formatter stringFromDate:trans.years.yearOfEvent];


    // set the value for our new object
    [newObject setValue:[dateFormatter stringFromDate:date] forKey:@"yearOfEvent"];

    // do the coupling of old and new
    [manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping];

    return YES;

所以在我的例子中,我从日期实体中提取“年份”,因为 yearOfEvent 现在是一个字符串。

此外,我不知道将其余代码放在迁移接受的答案中的什么位置。

为清楚起见,这是我的 NSFetchedResultsController 设置日期:

- (NSFetchedResultsController *)fetchedResultsController

    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
    if (_fetchedResultsController != nil)
    
        return _fetchedResultsController;
    
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Years" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;
    NSPredicate *d = [NSPredicate predicateWithFormat:@"transactions.years.@count !=0"];
    [fetchRequest setPredicate:d];
    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"yearOfEvent" ascending:NO];

    fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
    fetchRequest.fetchBatchSize = 20;
    NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;
    return _fetchedResultsController;


- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath

    EnvylopeDatesTableViewCell  *customCell = (EnvylopeDatesTableViewCell *)cell;
    Years *years = [self.fetchedResultsController objectAtIndexPath:indexPath];
    NSString *stringFromDate = years.yearOfEvent;
    customCell.datesLabel.text = stringFromDate;
    customCell.datesLabel.textColor = [UIColor whiteColor];

    UIView *customColorView = [[UIView alloc] init];
    customColorView.backgroundColor = [UIColor darkGrayColor];
    customCell.selectedBackgroundView = customColorView;


在 MappingModel 中,我为 YearstoYears 实体保留了默认值表达式,并确保将自定义策略设置为我创建的模型类 (Model.h/m)。

对此的任何指导将不胜感激。

注意事项 - 我有一个日期和年份选项卡的原因是为了在整个应用程序中进行格式化。我敢肯定,我可以只使用一个日期,然后将“年份”从中提取到一个 NSString 中。我会这样做,但现在,我只希望迁移能够正常工作。

重申一下,问题是,如果我正在运行旧版本的应用程序并迁移到这个新构建的版本,则“日期”选项卡不会正确设置年份,并且该年份的单元格为空白。

【问题讨论】:

【参考方案1】:

我之前没有进行过重量级迁移,而是更喜欢使用轻量级迁移。我祝贺你处理了这个详细的过程。

我强烈推荐一本书 - 来自 The Pragmatic Bookshelf – Marcus S. Zarra 的“Core Data, 2nd Edition, Data Storage and Management for ios, OS X, and iCloud”(2013 年 1 月),尤其是标题为“版本控制和迁移”的第 3 章

关于您关于此 SO 问题答案的代码最后部分的位置的问题...Example or explanation of Core Data Migration with multiple passes? 我怀疑该答案的最后部分代码应该放在您需要添加持久性之前存储(将您的NSPersistentStore 的URL)(在该示例中为NSURL *destinationStoreURL)到您的NSPersistentStoreCoordinator。也就是说,在您的核心数据堆栈中看起来类似于此的代码行之前......

NSPersistentStore *persistentStore = [self.persistentStoreCoordinator
                        addPersistentStoreWithType:NSSQLiteStoreType 
                        configuration:nil 
                        URL:destinationStoreURL 
                        options:options];

现在解决你的问题 -

“日期”选项卡未正确设置年份,并且该年份的单元格为空白

我有两个问题...

在您的NSFetchResultsController 运行后,您是否可以记录 (NSLog) fetchedObjects 的值以检查您是否真的从您的NSFetchedResultsController 接收任何数据?可能是数据转换不成功,你的FRC返回nil?例如:

NSLog(@"fetched objects are: %@", self.fetchResultsController.fetchedObjects);

另外,在您的 configureCell: 方法中,您是否能够记录局部变量 NSString * stringFromDate 的值?

让我知道您的进展情况...无论您是否从您的 NSFetchedResultsController 接收数据,以及这些数据是否已成功传递给 configureCell 方法。

一旦我们知道我的问题的答案,我希望能帮助您找到解决方案。

【讨论】:

非常感谢@Andrewbuilder 的回复 - 我已经在此处填写了详细信息,但我接受了您的回答,因为它看起来像是要走的路!手动迁移是多么可怕的任务!【参考方案2】:

这是对您的回答@AndrewBuilder 的回应。

感谢您的回答,并对迟到的回复深表歉意。进行自定义迁移是一种非常可怕的方法,尤其是因为我对开发还很陌生。但是,除了我的问题中的代码之外,我设法用一行代码修复了它。

当我回到我提到的答案时,我注意到他在转换之前有一行 valueForKey ,所以我就这样做了:

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance
                                      entityMapping:(NSEntityMapping *)mapping
                                            manager:(NSMigrationManager *)manager
                                              error:(NSError **)error

    // Create a new object for the model context
    NSManagedObject *newObject =
    [NSEntityDescription insertNewObjectForEntityForName:[mapping destinationEntityName]
                                  inManagedObjectContext:[manager destinationContext]];

    // do our transfer of nsdate to nsstring
    NSDate *date = [sInstance valueForKey:@"yearOfEvent"]; 
    //NSDate *date = [sInstance valueForKey:@"timeStamp"];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

    [dateFormatter setDateFormat:@"y"];
    //NSString *stringFromYear = [formatter stringFromDate:trans.years.yearOfEvent];


    // set the value for our new object
    [newObject setValue:[dateFormatter stringFromDate:date] forKey:@"yearOfEvent"];

    // do the coupling of old and new
    [manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping];

    return YES;

就我而言,关键是 yearOfEvent。

令人担忧或有趣的是,这就是我要做的所有事情。当我使用 yearOfEvent NSDate *date = [sInstance valueForKey:@"yearOfEvent"]; 这样做时,它立即开始工作。

我对我的方法持怀疑态度,因为我实际上并没有将其余代码放在他的答案中我的代码中的任何位置,但是从版本 1.0 到 1.2.0、1.1.1 到 1.2.0 和 1.0 的迁移工作到 1.1.1 到 1.2.0。

我必须弄清楚这里发生了什么!

【讨论】:

以上是关于从 NSDate 到 NSString 的转换,属性上的核心数据迁移未正确显示字符串的主要内容,如果未能解决你的问题,请参考以下文章

NSString 到 NSDate 的转换失败

将NSDate转换为NSString

将 NSDate 转换为 NSString

将NSString转换为NSDate(并再次返回)

NSDate和NSString的转换及判定是昨天,今天,明天

NSDate NSString时间字符串 NSTimeInterval 的转换