1

コア データに 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
4

1 に答える 1

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

これに何か問題があるかどうか教えてください。

于 2013-05-23T09:40:17.070 に答える