コア データに 1 対多の関係を保存しようとしています。新しい子リスト オブジェクトを新しい親オブジェクトに関連付ける必要があるかどうかを判断するには、ユーザーの判断が必要です。それ以外の場合は、既存のデータベース エントリが親オブジェクトとして使用されます。保存後、特定の状況下でアプリがクラッシュします。
最終編集:申し訳ありませんが、すべての編集を保持しておいていただければ幸いです。悟りのプロセスは非常に複雑でした。結局のところ、私はそれがデータの競合だと考え始めました...正しい方向に私を向けるのを手伝ってくれたTomNSFetchedResultsController
に再び感謝します. 現在、エンティティ クラスの有効なcompare:
メソッドを作成しましたが、これまでのところ、それが機能していることはわかりません。私は私の質問に対する答えを書こうとしています。これに関するあなたからの情報や警告には、引き続き非常に感謝しています!
編集 3:保存手順とユーザー アラートは、問題に付随しているようです。私は今NSFetchedResultsControllerにズームインし、関係(「検査」)をsectionNameKeyPath
. compare:
ここで、カテゴリ内のメソッドをExamination
エンティティ クラスに書き込もうとします。それでもうまくいかない場合はImage
、関係に加えて、比較可能な値をエンティティ クラスに書き込み、それをセクションに使用する必要があります。皆さんは賛成ですか?
編集 1:クラッシュは、ユーザーが新しい検査を希望するかどうかを尋ねられ、YES と答えた後にのみ発生します。ユーザー プロンプトがなかった場合 (新しい検査の作成が事実によって決定された場合 (検査が存在しない = YES、既存の検査がタイムアウトしていない = NO) の場合も、同じメソッドが入力されます。これらの場合、エラーは発生しません。アラート ビューが開いている間にビューの読み込みが終了し、コレクション ビューとその NSFetchedResultsController が結合する必要があります。
編集 2: Tomのおかげで、ここにコール スタックがあります。関連があるとは思いませんでしたが、View Controller はコレクション ビューに画像を表示し、検査ごとに画像のセクションが降順で表示されます。そのため、NSFetchedResultsController のセクション キーとソート記述子の両方が、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