CoreData, UIManagedDocument and empty Persistent S

2019-06-13 17:41发布

问题:

I'm using UIManagedDocument to reading and writing to CoreData. I have Document class. This is a document from some tutorial:

.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

typedef void (^OnDocumentReady) (UIManagedDocument *document);

@interface Document : NSObject

@property (strong, nonatomic) UIManagedDocument *document;

+ (Document *)sharedDocument;
- (void)performWithDocument:(OnDocumentReady)onDocumentReady;

@end

.m

@interface Document ()
- (void)objectsDidChange:(NSNotification *)notification;
- (void)contextDidSave:(NSNotification *)notification;
@end;

@implementation Document

@synthesize document = _document;

static Document*_sharedInstance;

+ (Document *)sharedDocument
{
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        _sharedInstance = [[self alloc] init];
    });

    return _sharedInstance;
}

- (id)init {
    self = [super init];
    if (self) {
        NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
        url = [url URLByAppendingPathComponent:@"Document"];
        self.document = [[UIManagedDocument alloc] initWithFileURL:url];

        NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                 [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                 [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
        self.document.persistentStoreOptions = options;

        // Register for notifications
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(objectsDidChange:)
                                                     name:NSManagedObjectContextObjectsDidChangeNotification
                                                   object:self.document.managedObjectContext];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(contextDidSave:)
                                                     name:NSManagedObjectContextDidSaveNotification
                                                   object:self.document.managedObjectContext];
    }
    return self;
}

- (void)performWithDocument:(OnDocumentReady)onDocumentReady
{
    void (^OnDocumentDidLoad)(BOOL) = ^(BOOL success) {
        onDocumentReady(self.document);
    };

    if (![[NSFileManager defaultManager] fileExistsAtPath:[self.document.fileURL path]]) {
        [self.document saveToURL:self.document.fileURL
                forSaveOperation:UIDocumentSaveForCreating
               completionHandler:OnDocumentDidLoad];
    } else if (self.document.documentState == UIDocumentStateClosed) {
        [self.document openWithCompletionHandler:OnDocumentDidLoad];
    } else if (self.document.documentState == UIDocumentStateNormal) {
        OnDocumentDidLoad(YES);
    }
}

- (void)objectsDidChange:(NSNotification *)notification
{
#ifdef DEBUG
    NSLog(@"NSManagedObjects did change.");
#endif
}

- (void)contextDidSave:(NSNotification *)notification
{
#ifdef DEBUG
    NSLog(@"NSManagedContext did save.");
#endif
}

@end

Then in ViewController i have NSURLConnection:

-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
    DataParser *parser = [[DataParser alloc] init];
    [parser startParsingData:self.myMutableData withContext:self.moc];
}

and i open the document here:

-(void)initDocument {
    if (!self.moc) {
        [[Document sharedDocument] performWithDocument:^(UIManagedDocument *document) {
            self.moc = document.managedObjectContext;
            [[NSNotificationCenter defaultCenter] postNotificationName:UIDocumentStateChangedNotification object:self];

            NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"someURL"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];
            [NSURLConnection connectionWithRequest:request delegate:self];
        }];
    }
}

Then i try to parse data:

-(void)startParsingData:(NSData*)data withContext:(NSManagedObjectContext*)context {
    self.moc = context;
    self.startDate = [NSDate date];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
    parser.delegate = self;
    [parser parse];
}

ant try to load it in core data after XML parsing:

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    if([elementName isEqualToString:@"Item"]) {
            [self.moc performBlockAndWait:^{
                TestEntity *te = [NSEntityDescription insertNewObjectForEntityForName:@"TestEntity" inManagedObjectContext:self.moc];
                te.surname = @"5433fds";
                te.name = @"5342fdsfsd";
            }];
    }
}

And i see the logs:

2014-01-24 16:21:21.692 Ce[85149:70b] NSManagedObjects did change.
2014-01-24 16:21:36.696 Ce[85149:70b] NSManagedContext did save.

So i assume that this should be in sqlite file, but when I try to read it is completely empty. awl file have some bigger size, but persistentStore have 0 rows. Why?

回答1:

What happens when you launch the app a second time, can you see the data in your app?

In iOS 7, SQLite now has journaling turned on. I wonder if the journalling is causing a confusion (data is there but not in the sqlite file yet).

I would also question why you are using a UIManagedDocument. It appears you are using Core Data in a Singleton pattern (which is bad) and then using UIManagedDocument in that singleton pattern (worse).

UIManagedDocument is intended for document based apps. You are probably running foul of some of the "automated" features inside of that class that would be easily cleared up if you built a standard Core Data stack instead.

You might consider turning Journaling off, adding a vacuum option to your NSPersistentStoreCoordinator or switch to a standard Core Data stack.



回答2:

This is likely because of a new SQLite journal mode used in iOS 7. If you set it to the old mode as shown in the answer to the following SO question you should see your data again.

How to disable WAL journal mode