1

我正在使用 Core Data 开发一个 iOS 应用程序。我有一个与,实体和与实体Log的关系具有一对多关系的实体。该日志还具有, ,属性。我可以创建日志、更改其属性、添加实体,这些更改会正确显示,直到我退出应用程序。所有的更改都会消失,而我正在查看 sqlite 数据库,所有这些更改都从未保存在数据库中。在数据库中,对象只会被创建,但不会链接到对象。AudioPhotoone-to-oneStatustextlongitudelatitudestatusstatuslog

但是如果我在 or 中添加一个audioorphoto对象,我对log.audioSetorlog.photoSet所做log的更改,包括对textor的更改status,都会突然保存到数据库中。

因此,似乎更改仅保留在NSManagedObjectContext, 直到添加相关的 one_to_many 实体并且[[LTLogStore sharedStore] saveChanges]将突然开始工作。

我正在使用单例来管理NSManagedObjectContext. 有任何想法吗?

如果相关,我会发布一些代码。谢谢。

更新:我不确定这些代码是否足够。但基本上一切正常,并显示,它只是不保存到数据库中。我正在使用mogenerator来设置textand latitude,但是因为一切都在上下文中。我不确定这是您可能需要的代码。

代码:

@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 回答