核心数据 - NSDate 属性在持久存储中发生变化

Posted

技术标签:

【中文标题】核心数据 - NSDate 属性在持久存储中发生变化【英文标题】:Core Data - NSDate property changing while in the persistent store 【发布时间】:2012-11-02 23:58:56 【问题描述】:

我正在开发一个日历应用程序,它可以保存和显示各种班次轮换。在最近发生的无法解释的错误与 X-Code 更新到 ver. 4.5.2.我在 Google 或 Stackoveflow 上的任何地方都找不到任何类似的问题。

班次轮换由用户设置和保存。创建轮班轮换的过程涉及向用户显示一系列日期,以及用户将每个日期与特定轮班相关联。

我正在使用带有 sqlite 存储的 Core Data 来管理保存的数据。上述元素的对象图涉及几个“Shift”NSManagedObject 实体,其中包含班次的详细信息,例如标题、开始和结束时间,最重要的是允许稍后显示班次轮换的日期。 “Shift”实体由另一个名为“ShiftRotation”的 NSManagedObject 保持在一起,它与“Shifts”具有多对一的关系。

我的应用程序一直正常工作,直到最近我对使用这些“Shift”对象的方式进行了一些更改。

我的问题是这个。用户将“班次”与日期相关联。日期存储在“Shift”NSManagedObject 的“日期”属性中。上下文被保存。当我稍后访问“Shift”时,没有进行任何更改,分配给“Shift”的日期似乎已更改。

我逐行检查了我的代码并添加了许多 NSLogs 来查看它在哪里或为什么会发生变化,但没有任何结果。我注意到在我将上下文保存在发生保存的类中之前和之后的日期没有变化。当我从主日历屏幕访问它时,也没有任何变化。但是,当我稍后从我管理所有班次轮换的另一个屏幕上检索“班次”时,它们似乎已经改变了。

日期更改大约是未来 31 到 32 天。我不禁想知道这是 Core Data 中的错误还是我遗漏了什么。

从 sqlite 持久存储中访问日期会导致重大且不可预测的日期更改的任何解释或原因?

更新:我尝试使用 NSNotificationCenter 进一步缩小正在发生的事情以及发生的位置。

我使用的是 ios 6。

我注册了根视图控制器来观察托管对象上下文的任何变化。特别是对对象的任何更改(即 NSManagedObjectContextObjectDidChange)。

在我更新“Shift”NSManagedObject 的属性后,我收到一条通知,指出“Shift”对象已更新。这是一些代码和日志。

这是创建“Shift”实体并指定日期的地方:

- (NSMutableArray *)arrayWithDateStartingAtDate:(NSDate *)anyDate forNumberOfDays:(NSUInteger)days;

    NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSMutableArray *monthArray = [[NSMutableArray alloc] init];
    NSDateComponents *comps = [[NSDateComponents alloc] init];
    NSDate *tempDate;

    NSManagedObjectContext *moc = [[CSEventStore sharedStore] managedObjectContext];

    for (NSUInteger i = 0; i < days; i++) 
        [comps setDay:i];
        tempDate = [gregorian dateByAddingComponents:comps toDate:anyDate options:0];
        Shift *shiftRDO = [Shift initShiftEntityRDOWithDate:tempDate InContext:moc];
        [shiftRDO setShiftRotation:_shiftRotation];

        NSMutableDictionary *dateDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:tempDate, @"date", shiftRDO, @"shift", nil];
        [monthArray addObject:dateDict];
    

    return monthArray;

这是调用更新它的地方:

[shiftEntity updateWithShiftType:_selectedShiftType inContext:context];

调用方法:

- (void)updateWithShiftType:(ShiftType *)shiftType inContext:(NSManagedObjectContext *)context

    self.title = [NSString stringWithFormat:@"%@",shiftType.title];
    self.symbol = [NSString stringWithFormat:@"%@",shiftType.symbol];
    self.startTime = [shiftType.startTime dateByAddingTimeInterval:0];
    self.endTime = [shiftType.endTime dateByAddingTimeInterval:0];
    self.splitStartTime = [shiftType.splitStartTime dateByAddingTimeInterval:0];
    self.splitEndTime = [shiftType.splitEndTime dateByAddingTimeInterval:0];
    self.isWorkday = [shiftType.isWorkday copy];
    self.isTimeOff = [shiftType.isTimeOff copy];
    self.typeID = [shiftType typeID];

以上代码执行后,日志中贴出第一个通知:

<Shift: 0x8140b70> (entity: Shift; id: 0x81409e0 <x-coredata:///Shift/tE59A199A-444E-4079-BD8C-0D3E734607783> ; data: 
date = "2012-10-29 04:00:00 +0000";
endTime = "2012-11-02 19:35:38 +0000";
isTimeOff = 0;
isWorkday = 1;
shiftRotation = "0x81406a0 <x-coredata:///ShiftRotation/tE59A199A-444E-4079-BD8C-0D3E734607782>";
splitEndTime = nil;
splitStartTime = nil;
startTime = "2012-11-02 09:35:34 +0000";
symbol = none;
title = Days;
typeID = "991ACC8C-ECE0-4555-9002-7AC233F26CBF";

date”属性确实有正确的分配日期。

现在只要我用以下方法保存上下文:

- (BOOL)saveContext

    NSError *error = nil;
    if (__managedObjectContext) 
        if ([__managedObjectContext hasChanges] && ![__managedObjectContext save:&error]) 
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);

            // More details on error
            NSArray* detailedErrors = [error userInfo][NSDetailedErrorsKey];
            if(detailedErrors != nil && [detailedErrors count] > 0) 
                for(NSError* detailedError in detailedErrors) 
                    NSLog(@"  DetailedError: %@", [detailedError userInfo]);
                
            
            else 
                NSLog(@"  %@", [error userInfo]);
            

            return NO;
            abort();
        
    
    return YES;

我收到带有以下日志的第二次更改通知:

    <Shift: 0x8140b70> (entity: Shift; id: 0x82c9dd0 <x-coredata://7CE834F9-5056-457A-BB2C-B8BA638086F1/Shift/p23> ; data: 
    date = "2012-12-02 05:00:00 +0000";
    endTime = "2012-11-02 19:35:38 +0000";
    isTimeOff = 0;
    isWorkday = 1;
    shiftRotation = "0x8260dc0 <x-coredata://7CE834F9-5056-457A-BB2C-B8BA638086F1/ShiftRotation/p11>";
    splitEndTime = nil;
    splitStartTime = nil;
    startTime = "2012-11-02 09:35:34 +0000";
    symbol = none;
    title = Days;
    typeID = "991ACC8C-ECE0-4555-9002-7AC233F26CBF";
),

如您所见,“日期”更改为 2012-12-02,只需保存即可。

更新:我终于能够追踪到无法解释的变化的原因。在我将上下文保存在代码中的其他位置之后,我似乎无意中更改了“日期”属性。如果我遵循卡明的惯例,也许就不会发生这种情况(如果我有名声,我会给出+1)。毕竟持久存储中的值没有改变。

也许分享我如何追踪这个错误可能会使其他可能有类似问题的人受益,不一定与 Core Data 相关。

我使用 Key Value Observing 来观察“日期”属性。我在创建“Shift”实体后立即开始观察:

[_shiftEntity addObserver:self forKeyPath:@"date" options:NSKeyValueObservingOptionNew context:NULL];

...并在我的代码中已经发生更改的某个点之后停止观察:

[_shiftEntity removeObserver:self forKeyPath:@"date" context:NULL];

然后我实现了 KVO 方法:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

    NSLog(@"Object being observed: %@", [object description]);
    NSLog(@"Value changed to: %@", change[NSKeyValueChangeNewKey]);

我在上面的方法中设置了一个断点。当变化发生时,它停在这一点上,并告诉我发生了什么变化。最重要的是,当代码停止执行时,我查看了调用堆栈,并追溯了导致更改的代码所采用的路径。我能够准确地找到我的代码进行更改的位置。

我对使用 KVO 的了解不多,但这次经历向我展示了它即使用于调试目的也很有用。

【问题讨论】:

您能否发布您的代码并告诉我们正在使用哪些 iOS? NSLog 获取/设置值的每个实例。也请发布您的代码。 【参考方案1】:

可能的命名冲突。我学会了给我所有的名字加上前缀。例如,我将核心数据属性称为“pDate”而不是“日期”。我称堆栈本地“zDate”、参数“aDate”、静态“sDate”和实例变量“iDate”。 (我没有为方法名称添加前缀。)这避免了变量命名冲突并使代码更易于理解。给某个东西起一个简单的名称是为了寻找与操作系统的名称冲突,它应该在其所有变量前面加上 __(双下划线),但这并不总是发生。这可能不是您的问题,但操作系统可能将“日期”一词用于其他目的,并且似乎将“日期”修改为保存时间。

【讨论】:

以上是关于核心数据 - NSDate 属性在持久存储中发生变化的主要内容,如果未能解决你的问题,请参考以下文章

核心数据:基于 NSDate 的时间分量过滤条目

NSPredicate 获取具有 NSDate 日期范围内的日期属性的核心数据对象

将 NSDate 存储在核心数据中(swift)

iOS数据持久化之---属性列表

如果我将模型中的属性类型从 NSString 更改为 NSDate,是不是需要核心数据迁移

Xcode Entity Core Data - NSDate 错误的年份? [复制]