核心数据 - 在持久性存储中更改NSDate属性

时间:2012-11-02 23:58:57

标签: objective-c cocoa-touch cocoa core-data

我正在开发一个日历应用程序,可以保存并显示各种班次轮换。在最近无法解释的错误之后的最近几天,我一直在撞墙,这与X-Code更新版本奇怪地吻合。 4.5.2。我无法在Google或Stackoveflow上找到任何类似的问题。

用户设置并保存换档旋转。创建班次轮换的过程包括向用户呈现一系列日期,以及用户将每个日期与特定班次相关联。

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

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

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

我逐行浏览了我的代码并添加了许多NSLog,以查看它的变化位置或原因,但没有结果。我注意到在保存发生的类中保存上下文之后,之前的日期没有变化。当我从主日历屏幕访问它时,也没有变化。然而,当我稍后从另一个屏幕检索“移位”时,我管理所有移位轮换,它们似乎已经改变。

日期变化约为未来31至32天。我不禁想知道这是核心数据中的错误还是我遗漏了什么。

为什么从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";

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

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

- (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,只需保存即可。

更新:我终于能够追踪无法解释的变化的原因。在我在代码中的其他地方保存上下文后,似乎无意中更改了“date”属性。如果我遵循carmin的惯例,也许就不会发生这种情况(如果我有声誉,我会给+1)。毕竟,该值在Persistent商店中没有变化。

也许分享我如何追踪这个错误可能会让其他可能有类似问题的人受益,而不一定与核心数据相关。

我使用Key Value Observing来观察“date”属性。我在创建“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知之甚少,但这种经验表明,即使是出于调试目的,它也很有用。

1 个答案:

答案 0 :(得分:0)

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