我正在开发一个日历应用程序,它可以保存和显示各种班次轮换。在最近发生的无法解释的错误与 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";
“日期”属性确实有正确的分配日期。
现在,只要我使用以下方法保存上下文:
- (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)。毕竟,持久存储中的值没有改变。
也许分享我如何追踪这个错误可能会使其他可能有类似问题的人受益,不一定与核心数据相关。
我使用键值观察来观察“日期”属性。创建“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 的知识,但这次经历向我展示了它即使用于调试目的也很有用。