NSFetchedResultsController:NSSortDescriptor以关系为密钥发送无法识别的选择器比较:到核心数据实体对象

时间:2013-05-22 16:18:38

标签: core-data foreign-keys one-to-many nssortdescriptor unrecognized-selector

我正在尝试在核心数据中保存一对多关系。涉及用户决定以确定是否需要将新子列表对象附加到新父对象。在另一种情况下,现有数据库条目用作父对象。在保存后的某些情况下,应用程序崩溃。

最终编辑:很抱歉,如果您介意保留所有编辑内容,我仍然愿意。启蒙的过程非常复杂。毕竟我开始认为这是一个数据冲突...再次感谢Tom,他帮助指出了我正确的方向:我仍在使用关系对核心数据实体进行排序和分组{{1 }}。我现在已经为我的实体类编写了一个有效的NSFetchedResultsController方法,并且到目前为止我能看到它正在工作。我即将为我的问题写一个答案。对于您的任何信息警告,我仍将非常感激!

编辑3:保存程序和用户警报似乎是问题的偶然。我现在放大了NSFetchedResultsController以及我使用关系('examination')作为compare:的事实。我现在尝试在类sectionNameKeyPath实体类中编写compare:方法。如果这也不起作用,除了关系之外,我还必须在我的Examination实体类中写一个可比较的值,并将其用于部分。 你们都同意吗?

编辑1:只有在询问用户是否需要新的检查并且回答“是”之后,才会发生崩溃。没有用户提示时也会输入相同的方法(当新事件的创建由事实决定时(没有检查存在=是,现有检查没有超时=否)。在这些情况下,错误不会发生必须是视图在警报视图打开时完成加载,然后集合视图及其NSFetchedResultsController加入乐趣。

编辑2:感谢Tom,这是调用堆栈。我不认为它是相关的,但视图控制器在集合视图中显示图像,每个检查的图像部分下降。因此,在发送MOC更改通知后,NSFetchedResultsController的section key和sort描述符都在使用检查。这不是崩溃我的应用程序的保存:它是NSSortDescriptor(或者,公平地说,我使用所有这些)。 image of the call stack


NSFetchedResultsController的代码:

Image

保存检查的代码(现有的或新的)和新图像:

#pragma mark - NSFetchedResultsController

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

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:NSStringFromClass([Image class]) inManagedObjectContext:[[HLSModelManager currentModelManager] managedObjectContext]];
    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];

    // Edit the sort key, identical sort to section key path must be first criterion
    NSSortDescriptor *examinationSortDescriptor = [[NSSortDescriptor alloc] initWithKey:kexaminationSortDescriptor ascending:NO];
    NSSortDescriptor *editDateSortDescriptor = [[NSSortDescriptor alloc] initWithKey:keditDateSortDescriptor ascending:NO];

    NSArray *sortDescriptors =[[NSArray alloc] initWithObjects:examinationSortDescriptor, editDateSortDescriptor, nil];

    [fetchRequest setSortDescriptors:sortDescriptors];

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[[HLSModelManager currentModelManager] managedObjectContext] sectionNameKeyPath:kSectionNameKeyPath cacheName:NSStringFromClass([Image class])];
    aFetchedResultsController.delegate = self;
    m_fetchedResultsController = aFetchedResultsController;

    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        HLSLoggerFatal(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return m_fetchedResultsController;
}

#pragma mark - NSFetchedResultsControllerDelegate - optional

/* Asks the delegate to return the corresponding section index entry for a given section name.  Does not enable NSFetchedResultsController change tracking.
 If this method isn't implemented by the delegate, the default implementation returns the capitalized first letter of the section name (seee NSFetchedResultsController sectionIndexTitleForSectionName:)
 Only needed if a section index is used.
 */
- (NSString *)controller:(NSFetchedResultsController *)controller sectionIndexTitleForSectionName:(NSString *)sectionName
{
    return sectionName;
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    // In the simplest, most efficient, case, reload the table view.
    [[self collectionView] reloadData];
}

/* THE OTHER DELEGATE METHODS ARE ONLY FOR UITableView! */

错误:

-(BOOL)saveNewImage
{
    BOOL done = NO;

    // remove observer for notification after alert
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kExaminationTimedoutAlertDone object:nil];

    Examination * currentExamination = [self getCurrentExamination];

    if ([self userWantsNewExamination] == YES)
    { // if an examination was found
        if (currentExamination != nil)
        { // if the found examination is not closed yet
            if ([currentExamination endDate] == nil)
            { // close examination & save!
                [currentExamination closeExamination];
                NSError *savingError = nil;
                [HLSModelManager saveCurrentModelContext:(&savingError)];

                if (savingError != nil)
                {
                    HLSLoggerFatal(@"Failed to save old, closed examination: %@, %@", savingError, [savingError userInfo]);
                    return NO;
                }
            }
        }
        currentExamination = nil;
    }

    // the examination to be saved, either new or old
    Examination * theExamination = nil;

    // now, whether user wants new examination or no current examination was found - new examination will be created
    if (currentExamination == nil)
    {
        // create new examination
        theExamination = [Examination insert];
        if (theExamination == nil)
        {
            HLSLoggerFatal(@"Failed to create new examination object.");
            currentExamination = nil;
            return NO;
        }

        // set new examinations data
        [theExamination setStartDate: [NSDate date]];
    }
    else
    {
        theExamination = currentExamination;
    }

    if (theExamination == nil)
    { // no image without examination!
        HLSLoggerFatal(@"No valid examination object.");
        return NO;
    }

    Image *newImage = [Image insert];

    if (newImage != nil)
    {
        // get users last name from app delegate
        AppDelegate * myAppDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];

        // set image data
        [newImage setEditUser: [[myAppDelegate user] lastName]];
        [newImage setEditDate: [NSDate date]];
        [newImage setExamination: theExamination];
        [newImage setImage: [self stillImage]];
        [newImage createImageThumbnail];

        // update edit data
        [theExamination setEditUser: [[myAppDelegate user] lastName]];
        [theExamination setEditDate: [NSDate date]];
        // unnecessary! CoreData does it automatically! [theExamination addImagesObject:newImage];

        //! Important: save all changes in one go!
        NSError *savingError = nil;
        [HLSModelManager saveCurrentModelContext:(&savingError)];

        if (savingError != nil)
        {
            HLSLoggerFatal(@"Failed to save new image + the examination: %@, %@", savingError, [savingError userInfo]);
        }
        else
        {
            // reload data into table view
            [[self collectionView] reloadData];
            return YES;
        }
    }
    else
    {
        HLSLoggerFatal(@"Failed to create new image object.");
        return NO;
    }

    return done;
}

以下是实体类文件:

2013-05-22 17:03:48.803 MyApp[11410:907] -[Examination compare:]: unrecognized selector sent to instance 0x1e5e73b0
2013-05-22 17:03:48.809 MyApp[11410:907] CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[Examination compare:]: unrecognized selector sent to instance 0x1e5e73b0 with userInfo (null)
2013-05-22 17:03:48.828 MyApp[11410:907] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Examination compare:]: unrecognized selector sent to instance 0x1e5e73b0'

//
//  Examination.h
//  MyApp
//

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@class Image;

@interface Examination : NSManagedObject

@property (nonatomic, retain) NSDate * editDate;
@property (nonatomic, retain) NSString * editUser;
@property (nonatomic, retain) NSDate * endDate;
@property (nonatomic, retain) NSDate * startDate;
@property (nonatomic, retain) NSSet *images;
@end

@interface Examination (CoreDataGeneratedAccessors)

- (void)addImagesObject:(Image *)value;
- (void)removeImagesObject:(Image *)value;
- (void)addImages:(NSSet *)values;
- (void)removeImages:(NSSet *)values;

@end

1 个答案:

答案 0 :(得分:1)

此错误与将数据保存到MOC无关。

因为在前一个视图控制器的prepareForSegue中触发了新图像数据的保存,并且用户警报为下一个视图控制器提供了完成加载的时间,同时创建了NSFetchedResultsController及其与其委托的连接,例外是在保存到MOC的临时环境中提出并且仅在用户警报之后。

NSFetchedResultsController仅在此情况下才开始侦听MOC的更改。似乎如果它收到MOC更改的警报,它将仅获取更改,然后才需要将新数据与现有数据进行比较。 非常欢迎有关此事的进一步信息!

然后,因为我已经为一个关系设置了一个排序描述符(以及sectionNameKeyPath)而没有提供在我的核心数据实体类中对实体对象进行排序的方法,所以NSFetchedResultsController无法继续。回顾它似乎都很容易和自然,我真的对我解决方案的简单性感到怀疑......

我觉得有趣的是,当没有任何变化干扰时,它可以一次性获取初始数据。毕竟它使用的是相同的NSSortDescriptor。 任何想法?

这是我的解决方案:

//
//  MyCategoryExamination.m
//  MyApp
//

#import "MyCategoryExamination.h"

@implementation Examination (MyCategoryExamination)

- (NSComparisonResult)compare:(Examination *)anotherExamination;
{
    return [[self startDate] compare:[anotherExamination startDate]];
}

@end

请告诉我这是否有问题。