我正在尝试在核心数据中保存一对多关系。需要用户决定是否需要将新的子列表对象附加到新的父对象。在另一种情况下,现有数据库条目用作父对象。在某些情况下保存后,应用程序会崩溃。
最终编辑:对不起,如果你介意我保留所有的编辑,我仍然会。启蒙的过程相当曲折。毕竟我开始认为这是一个数据冲突......再次感谢汤姆,他帮助我指出了正确的方向:我仍在使用关系对核心数据实体进行排序和分组NSFetchedResultsController
。我现在已经为我的实体类编写了一个有效的compare:
方法,到目前为止我所看到的它正在工作。我即将为我的问题写一个答案。我仍将非常感谢您就此提供的任何信息或警告!
编辑 3:保存过程和用户警报似乎与问题无关。我现在放大了 NSFetchedResultsController 以及我使用关系(“考试”)作为sectionNameKeyPath
. 我现在将尝试compare:
在我的Examination
实体类中编写一个类别中的方法。如果这也不起作用,Image
除了关系之外,我还必须在我的实体类中写入一个可比较的值,并将其用于部分。大家同意了吗?
编辑 1:只有在询问用户是否要进行新检查并回答“是”后,才会发生崩溃。当没有用户提示时也输入相同的方法(当事实决定创建新检查时(没有检查存在 = 是,现有检查未超时 = 否)。在这些情况下,不会发生错误. 一定是在alert view打开的时候view完成加载,然后collection view和它的NSFetchedResultsController一起玩。
编辑 2:感谢Tom,这是调用堆栈。我认为这无关紧要,但视图控制器在集合视图中显示图像,每次检查的图像部分递减。所以 NSFetchedResultsController 的 section key 和 sort 描述符都在使用 MOC 更改通知发送后的检查。使我的应用程序崩溃的不是保存:它是 NSSortDescriptor(或者,公平地说,我使用所有这些的方式)。
NSFetchedResultsController 的代码:
#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
//
// Examination.m
// MyApp
//
#import "Examination.h"
#import "Image.h"
@implementation Examination
@dynamic editDate;
@dynamic editUser;
@dynamic endDate;
@dynamic startDate;
@dynamic images;
@end