NSFetchedResultsController:索引x处的获取对象具有乱序部分名称

时间:2014-06-30 03:07:34

标签: uitableview core-data nssortdescriptor nsfetchedresultscontroller

我的tableView中有三个部分:

Today
Upcoming
Past  

当应用程序启动时,启动的视图控制器具有NSFetchedResultsController。如果我第一次删除应用程序并在模拟器中重新启动它,它运行得很好;但是,当日期发生变化时,意味着第二天,应用程序会给出以下错误:

2014-06-29 19:48:35.326 App[37398:4803] CoreData: error: (NSFetchedResultsController) The fetched object at index 4 has an out of order section name '  Upcoming. Objects must be sorted by section name'
2014-06-29 19:48:35.328 App[37398:4803] Unresolved error Error Domain=NSCocoaErrorDomain Code=134060 "The operation couldn’t be completed. (Cocoa error 134060.)" UserInfo=0xf990fc0 {reason=The fetched object at index 4 has an out of order section name '  Upcoming. Objects must be sorted by section name'}, {
    reason = "The fetched object at index 4 has an out of order section name '  Upcoming. Objects must be sorted by section name'";
}

指数各不相同。

NSFetchedResultsConroller设置:

- (NSFetchedResultsController *)fetchedResultsController
{
    if(_fetchedResultsController!=nil)
    {
        return  _fetchedResultsController;
    }

    if (!self.objectManager)
    {
        self.objectManager = [self getObjectManager];
    }
    self.managedObjectContext = self.objectManager.managedObjectStore.mainQueueManagedObjectContext;
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Meeting"
                                              inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

sortKey不是短暂的。我不确定它是否应该。

    NSSortDescriptor *firstSort = [[NSSortDescriptor alloc] initWithKey:@"sortKey"
                                                              ascending:YES];
    NSSortDescriptor *secondSort = [[NSSortDescriptor alloc] initWithKey:@"modifiedDate"
                                                               ascending:NO];


    NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:firstSort, secondSort, nil];

    [fetchRequest setSortDescriptors:sortDescriptors];


    self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest
                                                                       managedObjectContext:self.managedObjectContext
                                                                         sectionNameKeyPath:@"sectionIdentifier"
                                                                                  cacheName:nil];
    self.fetchedResultsController.delegate = self;
    return self.fetchedResultsController;
}

我按照Apple的示例代码创建了一个瞬态属性sectionIdentifiersortKey,以便按以下顺序显示部分:

https://developer.apple.com/library/ios/samplecode/DateSectionTitles/Listings/DateSectionTitles_APLEvent_m.html

   1. Today
   2. Upcoming
   3. Past 

如果对象不适用于任何部分,则不会显示该部分。我正在使用modifiedDate来确定对象所属的section

关于SO的大多数问题声称第一种排序应该是sectionIdentifier,但这是一种短暂的属性。另外,Apple的示例不使用sectionIdentifier作为第一种。有什么建议吗?

修改

这是我的NSManagedObject文件.m:

@dynamic primitiveSectionIdentifier, primitiveSortKey, primitiveModifiedDate, primitiveStartDate;

#pragma mark - Transient properties

- (NSString *)sectionIdentifier
{
    // Create and cache the section identifier on demand.

    [self willAccessValueForKey:@"sectionIdentifier"];
    NSString *tmp = [self primitiveSectionIdentifier];
    [self didAccessValueForKey:@"sectionIdentifier"];

    [self willAccessValueForKey:@"sortKey"];
    NSNumber *tempSort = [self primitiveSortKey];
    [self didAccessValueForKey:@"sortKey"];


    if (!tmp)
    {

        NSDate *dateToCompare = [self getcurrentTime:[self startDate]];
        NSCalendar* calendar = [NSCalendar currentCalendar];
        NSDate* now = [self getcurrentTime:[NSDate date]];
        NSDateFormatter *format = [[NSDateFormatter alloc] init];
        format.dateFormat = @"dd-MM-yyyy";
        format.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
        NSString *stringDate = [format stringFromDate:now];
        NSDate *todaysDate = [format dateFromString:stringDate];

        NSInteger differenceInDays =
        [calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSEraCalendarUnit forDate:dateToCompare] -
        [calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSEraCalendarUnit forDate:todaysDate];
        NSLog(@"differenceInDays %i", differenceInDays);

        NSString *sectionString;
        NSNumber *sortNumber;

        if (differenceInDays == 0)
        {
            sectionString = kSectionIDToday;
            sortNumber = [NSNumber numberWithInt:0];

        }
        else if (differenceInDays < 0)
        {
            sectionString = kSectionIDPast;
            sortNumber = [NSNumber numberWithInt:2];


        }
        else if (differenceInDays > 0)
        {
            sectionString = kSectionIDUpcoming;
            sortNumber = [NSNumber numberWithInt:1];
        }

        tmp = sectionString;
        tempSort = sortNumber;
        [self setPrimitiveSectionIdentifier:tmp];
        [self setPrimitiveSortKey:tempSort];    
    }

    return tmp;
}

-(NSDate *)getcurrentTime:(NSDate*)date
{
    NSDate *sourceDate = date;
    NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
    NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];

    NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:sourceDate];
    NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:sourceDate];
    NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset;

    NSDate* currentDate = [[NSDate alloc] initWithTimeInterval:interval sinceDate:sourceDate];
    return currentDate;
}


#pragma mark - Time stamp setter

- (void)setStartDate:(NSDate *)newDate
{
    // If the time stamp changes, the section identifier become invalid.
    [self willChangeValueForKey:@"startDate"];
    [self setPrimitiveStartDate:newDate];
    [self didChangeValueForKey:@"startDate"];

    [self setPrimitiveSectionIdentifier:nil];
}

- (void)setSortKey:(NSNumber *)sortKey
{
    // If the time stamp changes, the section identifier become invalid.
    [self willChangeValueForKey:@"sortKey"];
    [self setPrimitiveSortKey:sortKey];
    [self didChangeValueForKey:@"sortKey"];

    [self setPrimitiveSortKey:nil];
}


#pragma mark - Key path dependencies

+ (NSSet *)keyPathsForValuesAffectingSectionIdentifier
{
    // If the value of timeStamp changes, the section identifier may change as well.
    return [NSSet setWithObject:@"sortKey"];
}

+ (NSSet *)keyPathsForValuesAffectingSortKey
{
    // If the value of timeStamp changes, the section identifier may change as well.
    return [NSSet setWithObject:@"modifiedDate"];
}

修改2

我在模拟器中删除了应用并重新启动它。我第一次看到sortKey的值保存在Core Data中(我通过查看.sqllite文件验证了它)。第二天,当设备日期发生变化时,我重新启动应用程序,出现错误。当我查看CoreData时,sortKey值没有改变。我相信如果我每次启动应用程序时都可以刷新sortKey的值,那么错误可能就会消失。

编辑3

我从sectionIdentifier删除了(!tmp)以查看有帮助,但无效。 Core Data中没有数据更改。

- (NSString *)sectionIdentifier
{
    [self willAccessValueForKey:@"sectionIdentifier"];
    NSString *tmp = [self primitiveSectionIdentifier];
    [self didAccessValueForKey:@"sectionIdentifier"];

    [self willAccessValueForKey:@"sortKey"];
    NSNumber *tempSort = [self primitiveSortKey];
    [self didAccessValueForKey:@"sortKey"];

    NSDate *dateToCompare = [self getcurrentTime:[self startDate]];
    NSCalendar* calendar = [NSCalendar currentCalendar];
    NSDate* now = [self getcurrentTime:[NSDate date]];
    NSDateFormatter *format = [[NSDateFormatter alloc] init];
    format.dateFormat = @"dd-MM-yyyy";
    format.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
    NSString *stringDate = [format stringFromDate:now];
    NSDate *todaysDate = [format dateFromString:stringDate];

    NSInteger differenceInDays =
    [calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSEraCalendarUnit forDate:dateToCompare] -
    [calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSEraCalendarUnit forDate:todaysDate];

    NSString *sectionString;
    NSInteger sortNumber = 0;

    if (differenceInDays == 0)
    {
        sectionString = kSectionIDToday;
        sortNumber = 0;
    }
    else if (differenceInDays < 0)
    {
        sectionString = kSectionIDPast;
        sortNumber = 2;
    }
    else if (differenceInDays > 0)
    {
        sectionString = kSectionIDUpcoming;
        sortNumber = 1;
    }

    if (sortNumber != [tempSort integerValue])
    {
        tmp = sectionString;
        tempSort = [NSNumber numberWithInt:sortNumber];
        [self setPrimitiveSectionIdentifier:tmp];
        [self setPrimitiveSortKey:tempSort];
    }    
    return tmp;
}

2 个答案:

答案 0 :(得分:1)

您的主要问题似乎是触发更新过程以运行和修改您的数据存储内容。

当您更新sectionIdentifiersortKey时,您可以存储计算它们的日期,并比较请求检查日期仍然有效的日期。这使您可以轻松地检查在收到触发器时是否需要进行任何更新,以告知您某些内容已发生更改并且您需要进行验证。

对于触发器,您需要考虑应用程序到达前台,重要时间更改以及应用程序打开时的更改日期。因此,您可以从应用程序委托(或观察激活)触发,并且您可以观察UIApplicationSignificantTimeChangeNotification并且您可以配置计时器(当应用程序被激活时),该计时器倒计时到当天结束时(在应用程序转到后台)。

现在你有了触发器和动作。但是,当收到触发器时,您的视图控制器将不会始终存在,因此请考虑使用数据更新控制器来管理此过程。

答案 1 :(得分:1)

我很确定您的排序代码可以大大简化。但也许这是一个不同的问题。无论如何,目前尚不清楚,如果设置startDate并且有太多变量使得算法非常复杂。你能尝试使用日期和部分标识符进行重构吗?部分标识符应以正确的方式排序 - 您可以添加逻辑以在其他位置显示某些自定义字符串。

这是你应该尝试的东西。消除if (!tmp)检查 - 为了我的目的,我很幸运地修复了这个示例代码。

最后,目前尚不清楚如何在日期更改时更新FRC。确保您希望更改的数据按预期更新。