Do I need to tell core data to save to disk?

Discussion in 'iOS Programming' started by whitedragon101, Mar 24, 2015.

  1. whitedragon101 macrumors 6502a

    Joined:
    Sep 11, 2008
    #1
    I have been using core data like this (see below).

    Is this enough or am I missing an extra step? Does this last section (named //I SAVE THE CHANGES) actually save the data to the disk or just to memory? Do I need another command here to save it to disk?

    Many thanks

    Code:
    // I GET A CONTEXT
    
        AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate]; 
        _managedObjectContext = [appDelegate managedObjectContext];
    
    // I FETCH MY CURRENT RECORD (THE ONE THE USER IS USING)
        
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription
                                       entityForName:@"MyRecord" inManagedObjectContext:_managedObjectContext];
        [fetchRequest setEntity:entity];
        [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"isCurrentRecord == TRUE"]];
        NSError *error = nil;
        NSArray *tempArray =  [_managedObjectContext executeFetchRequest:fetchRequest error:&error]; 
        _myRecord = [tempArray objectAtIndex:0];
    
    // I MAKE CHANGES
    
        _myRecord.string1= @"hello";
    
    
    // I SAVE THE CHANGES
    
    NSError *errorA;
        
        if (![_managedObjectContext save:&errorA])
        {
            NSLog(@"Failed to update - error: %@", [errorA localizedDescription]);
        }
        else
        {
            NSLog(@"entity updated");
        }
    
    
     
  2. North Bronson macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #2
    It's sort of a chicken-and-egg problem. Your correctly saving an empty context because the fetch request never returned an object. Actually, you will crash when you try to access the first item of the array that will always be empty.

    Have you read Core Data Programming Guide?
     
  3. whitedragon101, Mar 25, 2015
    Last edited: Mar 25, 2015

    whitedragon101 thread starter macrumors 6502a

    Joined:
    Sep 11, 2008
    #3
    The above code does work. The fetch request returns an object, I can access its variables and the save function works. If I close the app and restart the phone the data is still there.

    However I want to make sure the app is resistant to data loss if the phone or app crashes and I not sure if the save command I use in the code above puts everything on disk then and there or puts it in memory to be saved in the background at some other time.

    edit: I have just read through the Core Data Programming guide and am still not sure when/how the save to persistent store occurs.
     
  4. North Bronson macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #4
    If that code is the only place in your app you are accessing Core Data, you will never have an object. There must be someplace else in your app you are creating a managed object. OK.

    The CDPG is a little outdated when it comes to a few things. Check out the documentation for
    Code:
    NSManagedObjectContext
    for a little more about how contexts save to store coordinators and how parent contexts work. It is not clear from your code how your context is being created.

    FWIW, I have seen the
    Code:
    save:
    method block while the save occurs if the context is set pointing directly to an appropriate store coordinator.
     
  5. whitedragon101 thread starter macrumors 6502a

    Joined:
    Sep 11, 2008
    #5
    I am using the tick box Xcode provides to use CoreData when I start my project, and then Xcode generates a load of handling code in the App Delegate. I then get the context from the delegate when I need it using the code in my first post. The auto generated code in the delegate is (scroll to about half way down):

    Code:
    //
    //  AppDelegate.m
    //  My App
    //
    //  Created by me on 20/02/2014.
    //  Copyright (c) 2014 me. All rights reserved.
    //
    
    #import "AppDelegate.h"
    
    @implementation AppDelegate
    
    @synthesize managedObjectContext = _managedObjectContext;
    @synthesize managedObjectModel = _managedObjectModel;
    @synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        //self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        // Override point for customization after application launch.
        //self.window.backgroundColor = [UIColor whiteColor];
        //[self.window makeKeyAndVisible];
        
        return YES;
    }
    
    - (void)applicationWillResignActive:(UIApplication *)application
    {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }
    
    - (void)applicationDidEnterBackground:(UIApplication *)application
    {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }
    
    - (void)applicationWillEnterForeground:(UIApplication *)application
    {
        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    }
    
    - (void)applicationDidBecomeActive:(UIApplication *)application
    {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }
    
    - (void)applicationWillTerminate:(UIApplication *)application
    {
        // Saves changes in the application's managed object context before the application terminates.
        [self saveContext];
    }
    
    - (void)saveContext
    {
        NSError *error = nil;
        NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
        if (managedObjectContext != nil) {
            if ([managedObjectContext hasChanges] && ![managedObjectContext save:&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. 
                NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                abort();
            } 
        }
    }
    
    #pragma mark - Core Data stack
    
    // Returns the managed object context for the application.
    // If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
    - (NSManagedObjectContext *)managedObjectContext
    {
        if (_managedObjectContext != nil) {
            return _managedObjectContext;
        }
        
        NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
        if (coordinator != nil) {
            _managedObjectContext = [[NSManagedObjectContext alloc] init];
            [_managedObjectContext setPersistentStoreCoordinator:coordinator];
        }
        return _managedObjectContext;
    }
    
    // Returns the managed object model for the application.
    // If the model doesn't already exist, it is created from the application's model.
    - (NSManagedObjectModel *)managedObjectModel
    {
        if (_managedObjectModel != nil) {
            return _managedObjectModel;
        }
        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CBT_0_2" withExtension:@"momd"];
        _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
        return _managedObjectModel;
    }
    
    // Returns the persistent store coordinator for the application.
    // If the coordinator doesn't already exist, it is created and the application's store added to it.
    - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
    {
        if (_persistentStoreCoordinator != nil) {
            return _persistentStoreCoordinator;
        }
        
        NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CBT_0_2.sqlite"];
        
        NSError *error = nil;
        _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
        if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&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. 
             
             Typical reasons for an error here include:
             * The persistent store is not accessible;
             * The schema for the persistent store is incompatible with current managed object model.
             Check the error message to determine what the actual problem was.
             
             
             If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
             
             If you encounter schema incompatibility errors during development, you can reduce their frequency by:
             * Simply deleting the existing store:
             [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
             
             * Performing automatic lightweight migration by passing the following dictionary as the options parameter:
             @{NSMigratePersistentStoresAutomaticallyOption:@YES, NSInferMappingModelAutomaticallyOption:@YES}
             
             Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
             
             */
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }    
        
        return _persistentStoreCoordinator;
    }
    
    #pragma mark - Application's Documents directory
    
    // Returns the URL to the application's Documents directory.
    - (NSURL *)applicationDocumentsDirectory
    {
        return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    }
    
    @end
    
     
  6. Mascots macrumors 65816

    Mascots

    Joined:
    Sep 5, 2009
    #6
    You should be fine.

    When you are closing the app and reopening it, the Core Data results that are stored in memory are released and the next time you fetch (the next open) it has to hit the Store Coordinator to retrieve the records - so they are for sure saving to disk.


    But here is a little note about saving context that may clear things up for you: When saving the Managed Object Context, if there are no parents, the changes will be automatically changed on disk (you don't need to worry about lost data at this point).
    However, if there is a parent context, the changes you make will be pushed up the chain to the parent MOC on save, but in this case, the parent won't be automatically "saved" so you need to manually save the parent to push those changes to the disk. This is the only time you should ever have to "double call" a save to apply your modifications to disk.
     

Share This Page