On creating a new application through Xcode, it simplifies the task of the developer by generating several files for us that are required for successful execution of the application. The two important files that are autogenerated by Xcode are application delegate files. The question is what is the usage of application delegate files? The answer is quite simple: there are several important events that happen in the life of an application. The two most important events are launching and termination of the application. The application needs to know when these events happen or are about to happen. The iPhone OS notifies about these events through Application Delegate by calling its appropriate methods. iPhone OS calls the applicationDidFinishLaunching
method when it finishes the launch procedure and calls applicationWillTerminate
when the application is terminated, so as to close any open files.
Let's have a look at the code of the autogenerated files. We begin with the header file of Application Delegate:
The header file of Application Delegate, progAppDelegate.h
contains some default code, as shown in the following code listing:
// probAppDelegate.h // prob #import <UIKit/UIKit.h> #import <CoreData/CoreData.h> @interface probAppDelegate : NSObject <UIApplicationDelegate> { UIWindow *window; UINavigationController *navigationController; @private NSManagedObjectContext *managedObjectContext_; NSManagedObjectModel *managedObjectModel_; NSPersistentStoreCoordinator *persistentStoreCoordinator_; } @property (nonatomic, retain) IBOutlet UIWindow *window; @property (nonatomic, retain) IBOutlet UINavigationController *navigationController; @property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel; @property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator; - (NSURL *)applicationDocumentsDirectory; - (void)saveContext; @end
The preceding file declares instance variables of the NSManagedObjectModel, NSManagedObjectContext
, and the NSPersistentStoreCoordinator
class. Also, all of the variables including an NSString
instance are defined as properties with the two attributes: retain
and nonatomic
. The retain
attribute informs the compiler to retain (keep) the instance variable and not to flush from memory while being used. The nonatomic
attribute informs the compiler that when it will be asked to generate the accessor and mutator methods of the outlets (synthesized), it should generate them without any additional code of implementing multithreading. The nonatomic
attribute is used for simple applications.
Let us also see the autogenerated code in Application Delegate's implementation file: probAppDelegate.m.
The implementation file of Application Delegate, progAppDelegate.m,
contains some default code, as shown in the following code listing:
// probAppDelegate.m // prob #import "probAppDelegate.h" #import "RootViewController.h" @implementation probAppDelegate @synthesize window; @synthesize navigationController; - (void)awakeFromNib { RootViewController *rootViewController = (RootViewController *) [navigationController topViewController]; rootViewController.managedObjectContext = self.managedObjectContext; } - (BOOL)application:(UIApplication *) application didFinishLaunchingWithOptions:(NSDictionary *) launchOptions { [self.window addSubview:navigationController.view]; [self.window makeKeyAndVisible]; return YES; }
In awakeFromNib
method, the root view controller of the application is set equal to the top view controller of the navigation controller. In applicationDidFinishLaunching
method, the view of the navigation controller is set as a subview of the content view and hence is displayed to the user.
- (void)applicationWillTerminate:(UIApplication *)application { [self saveContext]; } - (void)saveContext { NSError *error = nil; NSManagedObjectContext *managedObjectContext = self.managedObjectContext; if (managedObjectContext != nil) { if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } } }
The applicationWillTerminate
method is invoked just before exiting from the application and is usually used for saving the modifications applied to the managed object via managed object context. That is, the managed object context (containing the modifications applied to the managed object) is saved to the persistent store. The save action
method is used for saving the managed object context to the persistent store, that is, the changes made to the managed object context are committed through this method. An error will be displayed if there occurs some problem while saving the managed object context.
- (NSManagedObjectContext *)managedObjectContext { if (managedObjectContext_ != nil) { return managedObjectContext_; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { managedObjectContext_ = [[NSManagedObjectContext alloc] init]; [managedObjectContext_ setPersistentStoreCoordinator:coordinator]; } return managedObjectContext_; }
Every application has at least one managed object context. The managed object context maintains the state of the managed object after it is loaded in memory. All modifications that we apply to the managed object are actually applied to the managed object context. It keeps track of all the changes made to the managed object since the last time it was loaded in memory and hence helps in undoing any changes made to the managed object (if required). When we want to commit the modifications made to the managed object, we save the managed object context to the persistent store.
The managed contexts are not thread-safe, which means there are many chances of faults when managed object contexts are shared between threads or are accessed from multiple threads simultaneously. To avoid any conflicts, we need to apply the Locking mechanism. In other words, contexts in general should not be shared between threads. Apple's advice is to use one context per thread and to merge changes.
In the preceding method, a check is made to see if the instance variable, managedObjectContext
, already exists or not (nil value means the instance variable does not exist). If the instance variable exists, it is returned, else it is created. In order to deal with persistent store, the managed object context needs a reference to a PersistentStoreCoordinator
. Recall that the PersistentStoreCoordinator
is the essential middle layer in the stack that helps in storing and retrieving the managed object model from the persistent store (in the form of managed object context). So, to create the managedObjectContext
instance variable, we check for a pointer to the NSPersistentStoreCoordinator
. If the pointer to PersistentStoreCoordinator
exists, we create a new managedObjectContext
and after linking it with the pointer to the PersistentStoreCoordinator
, return it.
- (NSManagedObjectModel *)managedObjectModel { if (managedObjectModel_ != nil) { return managedObjectModel_; } NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"prob" ofType:@"momd"]; NSURL *modelURL = [NSURL fileURLWithPath:modelPath]; managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; return managedObjectModel_; }
This method first checks if the instance variable managedObjectModel
(of NSManagedObjectModel
class) already exists or not (nil value means the instance variable does not exist). If the instance variable exists, it is returned, or else it is created by merging all the data models found in the application bundle. It also means that we can have several data models (created in separate .xcdatamodelfiles)
in an application. All the data models (entities and their relationships) contained in different files will be merged (combined) into a single managed object model.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if (persistentStoreCoordinator_ != nil) { return persistentStoreCoordinator_; } NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"customersdata.sqlite"]; NSError *error = nil; persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return persistentStoreCoordinator_; }
The NSPersistentStoreCoordinator
class is meant for storing and retrieving the managed object model from the persistent store. It also helps in avoiding redundancy if multiple calls are made by different classes on the same file at the same time. The multiple calls are serialized by the NSPersistentStoreCoordinator
class to avoid redundancy.
The preceding code first checks whether the instance variable, persistentStoreCoordinator
, exists or not. If it exists, it is returned, or else it is created. Because we want to store the names of the customers in the file: customersdata.sqlite
, we define a path to the file in the Documents
directory of our application's sandbox.
We want the format of the persistent store to be of SQLite type, hence the parameter NSSQLiteStoreType
is passed to the method to specify the type of the persistent store. NSSQLiteStoreType
is a constant that tells Core Data to use a SQLite database for its persistent store. If we want to store the information in binary format, we may use the constant, NSBinaryStoreType
for the persistent store. If anything goes wrong while creating the instance variable persistentStoreCoordinator
, an error will be displayed:
- (NSURL *)applicationDocumentsDirectory { return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; }
This method is for finding the location to store the persistent store file. In Xcode, each application has its own sandboxed Documents
directory designed for the storage of files. So, we retrieve a list of the cache directories from the SearchPathForDirectoriesInDomains
method and write code to find the Documents
folder specific to our application.
- (void)dealloc { [managedObjectContext_ release]; [managedObjectModel_ release]; [persistentStoreCoordinator_ release]; [navigationController release]; [window release]; [super dealloc]; } @end
The dealloc
method is for releasing the memory assigned to different instance variables.
As seen in the previous code we specify the file: customersdata.sqlite
for storing the names of the customers in the NSURL
statement of the persistentStoreCordinator
method.
18.116.10.248