1

Core Data を使用して iOS アプリを開発しています。、エンティティ、およびエンティティとの関係Logとの一対多の関係を持つエンティティがあります。ログには、、、プロパティもあります。ログを作成し、そのプロパティを変更し、エンティティを追加すると、アプリケーションを終了するまで、これらの変更が正しく表示されます。すべての変更が消え、sqlite データベースを見ていましたが、これらの変更はすべてデータベースに保持されませんでした。データベースでは、オブジェクトが作成されるだけで、オブジェクトにリンクされません。AudioPhotoone-to-oneStatustextlongitudelatitudestatusstatuslog

しかし、audioorphotoオブジェクトをorに追加すると、 log.audioSetorへlog.photoSetの変更をlog含め、 に対して行った変更が突然データベースに保存されます。textstatus

NSManagedObjectContextそのため、関連する one_to_many エンティティが追加され、[[LTLogStore sharedStore] saveChanges]が突然機能し始めるまで、変更は でのみ維持されるようです。

を管理するためにシングルトンを使用していNSManagedObjectContextます。何か案は?

関連する場合は、いくつかのコードを投稿します。ありがとう。

更新: これらのコードで十分かどうかはわかりません。しかし、基本的にすべてが機能し、表示されますが、データベースに保存されません。を使用してandmogeneratorを設定していますが、すべてがコンテキスト内にあるためです。これが必要なコードかどうかはわかりません。textlatitude

コード:

@interface LTLogStore : NSObject{
}

+ (LTLogStore *)sharedStore;

- (void)removeItem:(Log *)p;

- (Log *)createItem;

- (BOOL)saveChanges;

@property(nonatomic, strong) NSFetchedResultsController *resultsController;
@property(nonatomic, strong) NSManagedObjectModel *model;
@property(nonatomic, strong) NSManagedObjectContext *context;

@end


@implementation LTLogStore
@synthesize resultsController;
@synthesize context, model;

+ (LTLogStore *)sharedStore
{
    static LTLogStore *sharedStore = nil;
    if(!sharedStore){
        sharedStore = [[super allocWithZone:nil] init];
    }

    return sharedStore;
}

+ (id)allocWithZone:(NSZone *)zone
{
    return [self sharedStore];
}

- (id)init 
{
    self = [super init];
    if(self) {                
        model = [NSManagedObjectModel mergedModelFromBundles:nil];

        NSPersistentStoreCoordinator *psc = 
        [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

        // Where does the SQLite file go?    
        NSString *path = [self itemArchivePath];
        NSURL *storeURL = [NSURL fileURLWithPath:path]; 

        NSError *error = nil;

        if (![psc addPersistentStoreWithType:NSSQLiteStoreType 
                               configuration:nil
                                         URL:storeURL
                                     options:nil
                                       error:&error]) {
            [NSException raise:@"Open failed"
                        format:@"Reason: %@", [error localizedDescription]];
        }

        // Create the managed object context
        context = [[NSManagedObjectContext alloc] init];
        [context setPersistentStoreCoordinator:psc];

        // The managed object context can manage undo, but we don't need it
        [context setUndoManager:nil];

    }
    return self;
}



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

    NSFetchRequest *request = [[NSFetchRequest alloc] init];

    NSEntityDescription *e = [[model entitiesByName] objectForKey:@"Log"];
    [request setEntity:e];

    NSSortDescriptor *sd = [NSSortDescriptor 
                            sortDescriptorWithKey:@"created_at"
                            ascending:NO];
    [request setSortDescriptors:[NSArray arrayWithObject:sd]];
    [request setReturnsObjectsAsFaults:NO];


    NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc]
                                                            initWithFetchRequest:request 
                                                            managedObjectContext:context
                                                            sectionNameKeyPath:nil cacheName:@"Root"];

    NSError *error;
    BOOL success = [fetchedResultsController performFetch:&error];
    if (!success) {
        //handle the error
    }

    return fetchedResultsController;
} 



- (NSString *)itemArchivePath
{
    NSArray *documentDirectories =
    NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                        NSUserDomainMask, YES);

    // Get one and only document directory from that list
    NSString *documentDirectory = [documentDirectories objectAtIndex:0];

    NSString *storePath = [documentDirectory stringByAppendingPathComponent:@"store.data"];
    return storePath;
}


- (BOOL)saveChanges
{
    NSError *err = nil;
    BOOL successful = [context save:&err];
    NSLog(@"Saving changes to the database");
    if (!successful) {
        NSLog(@"Error saving: %@", [err localizedDescription]);
    }
    return successful;
}

- (void)removeItem:(Log *)l
{
    [context deleteObject:l];
    [self saveChanges];
}



- (Log *)createItem
{    
    Log *p = [NSEntityDescription insertNewObjectForEntityForName:@"Log"
                                            inManagedObjectContext:context];
    [self saveChanges];
    return p;
}

@end



@interface Log : _Log {
}

//these two are some custom convenience methods for location attributes, but it does the work of setting the longitude and latitude value in the log object, but calling the [[LTLogStore sharedStore] saveChanges] still won't save it into the database.
-(CLLocation*)location;
-(void)setLocation:(CLLocation*)location;

//this all works 
-(Audio*)newAudio;
-(Audio*)newAudioWithPath:(NSString*)audioPath;
//after calling this method, even the log.text changes will be saved to the database.
-(void)addAudioWithPath:(NSString*)audioPath;    
-(void)removeAudio:(Audio*)audio;

@end



#import "Log.h"
#import "Audio.h"
#import "LTLogStore.h"

@implementation Log

-(CLLocation*)location{
    if (!self.longitude || !self.latitude) {
        return nil;
    }
    CLLocation *l = [[CLLocation alloc] initWithLatitude:[self.latitude doubleValue] longitude:[self.longitude doubleValue]];
    return l;
}

-(void)setLocation:(CLLocation*)location{
    if (location==nil) {
        self.latitude = nil;
        self.longitude = nil;
    }
    self.latitude = [NSNumber numberWithDouble: location.coordinate.latitude];
    self.longitude = [NSNumber numberWithDouble:location.coordinate.longitude];
    [[LTLogStore sharedStore] saveChanges];
}


-(Audio*)newAudio{
    Audio *a = [Audio new];
    a.log = self;
    return a;
}

-(Audio*)newAudioWithPath:(NSString*)audioPath{
    Audio *new = [self newAudio];
    [new setKey:audioPath];
    return new;
}

-(void)addAudioWithPath:(NSString*)audioPath{
    Audio *new = [self newAudio];
    [new setKey:audioPath];
    [[LTLogStore sharedStore] saveChanges];
}

-(void)removeAudio:(Audio*)audio{
    [self.audiosSet removeObject:audio];
    [[[LTLogStore sharedStore] context] deleteObject:audio];
    [[LTLogStore sharedStore] saveChanges];
}

@end

アップデート:

問題は解決しました。回答を参照してください。

更新の質問: オーバーライドが問題を引き起こしているのはなぜですか? Core Data や KVO の背後にある魔法の背後にある原因を誰か説明できますか?

4

1 に答える 1

0

問題は解決しました。Log クラスの willChangeValueForKey メソッドをオーバーライドしたため、問題が発生しました。コードは無関係だと思いました。しかし、それは次のとおりです。

- (void)willChangeValueForKey:(NSString *)key{
    //I added the following line to fix my problem
    [super willChangeValueForKey:key];

    //this is the original line, I want to have this 
    //because I want to have a isBlank property 
    //so I can see if the user modified the log
    _isBlank = false;

    //I tried to also add the following line to be safe.
    //turns out this line is not needed, and it will make the problem occur again
    //[super didChangeValueForKey:key];

}
于 2012-07-30T09:50:48.353 に答える