(转)数据持久化(一)Core Data的简单使用
写程序的过程一般都会涉及到数据的持久化保存,对于一个供用户使用的应用可以说是必备功能,保存数据的方法有几种:数据归档,写入磁盘文件,使用数据库,使用Core Data。
我也是最近才开始正式地接触Core Data,下面来说一下使用Core Data的学习过程。
写了两个简单的Demo来说一下其使用方法:
(一)SimpleCoreData_Demo:存取一些简单的数据类型
1.新建工程,使用Empty Application模板,点选使用Core Data选项:
和一般的工程的不同之处在于:
(1)工程自动加入了CoreData.framework:
(2)在工程中会生成一个xcdatamodeld文件:
(3)在委托类中自动生成以下代码:(在这里的注释参考了http://blog.****.net/ryantang03/article/details/7794226)
接口部分:
- #import <UIKit/UIKit.h>
- @interface AppDelegate : UIResponder <UIApplicationDelegate>
- @property (strong, nonatomic) UIWindow *window;
- @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext; // 托管对象上下文
- @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; // 托管对象模型
- @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; // 持久性存储区
- - (void)saveContext; // 保存上下文
- - (NSURL *)applicationDocumentsDirectory; // 获取程序数据存放文档的路径
- @end
实现部分:
- - (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;
- }
- // 获取SimpleCoreData_Demo.xcdatamodeld文件的路径
- NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"SimpleCoreData_Demo" withExtension:@"momd"];
- // 通过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:@"SimpleCoreData_Demo.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];
- // 例如:file:///Users/guo/Library/Application%20Support/iPhone%20Simulator/7.0/Applications/A4601DF3-A51D-4D42-B314-B3A63F5111C2/Documents/
- }
2.编辑xcdatamodeld文件,例如添加entity对象,为entity对象添加属性,设置entity属性之间的关系等。
在这里只是简单地添加了一个Data名的Entity,只有一个属性title,是NSString类型。
3.生成Entity对应的托管对象子类:
这样基本的存储数据模型就构建好了,可以在程序中对其进行操作。
4.在托管对象上下文中存取数据:
- #import <UIKit/UIKit.h>
- #import "AppDelegate.h"
- @interface ViewController : UIViewController
- @property (weak, nonatomic) IBOutlet UITextField *textField; // 输入Data类的title
- - (IBAction)addData:(id)sender; // 添加数据到数据库中
- - (IBAction)queryData:(id)sender; // 查询数据库中的数据
- @property (strong, nonatomic) AppDelegate *myDelegate; // 该视图控制器的委托类,用于获取本程序的Core Data相关类
- @end
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- // 首先获取本程序的委托,便于获取本程序的Core Data相关类对象
- self.myDelegate = [[UIApplication sharedApplication] delegate];
- UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard:)];
- [self.view addGestureRecognizer:tap];
- }
- - (void)didReceiveMemoryWarning
- {
- [super didReceiveMemoryWarning];
- // Dispose of any resources that can be recreated.
- }
- - (IBAction)addData:(id)sender {
- // 从托管对象上下文中获取Data类对象
- Data *data = (Data *)[NSEntityDescription insertNewObjectForEntityForName:@"Data" inManagedObjectContext:self.myDelegate.managedObjectContext];
- data.title = self.textField.text; // 设置data的title属性
- NSError *error = nil;
- BOOL isSaveSuccess = [self.myDelegate.managedObjectContext save:&error]; // 保存修改后的托管对象上下文
- if (isSaveSuccess && error == nil) {
- NSLog(@"Saving succeed");
- }
- else {
- NSLog(@"%@", error);
- }
- }
- - (IBAction)queryData:(id)sender {
- // 创建Fetch请求
- NSFetchRequest *request = [[NSFetchRequest alloc] init];
- NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Data" inManagedObjectContext:self.myDelegate.managedObjectContext];
- [request setEntity:entityDescription]; // 设置请求对象为名为Data的Entity
- // 对搜索结果进行排序
- NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:NO];
- NSArray *sortDescriptions = [[NSArray alloc] initWithObjects:sort, nil];
- [request setSortDescriptors:sortDescriptions];
- NSError *error = nil;
- // 获取Fetch请求得到的结果,是一个数组
- NSArray *fetchResults = [self.myDelegate.managedObjectContext executeFetchRequest:request error:&error];
- if (fetchResults) {
- // 输出数组中各个元素的title
- for (Data *data in fetchResults) {
- NSLog(@"title:%@", data.title);
- }
- }
- else {
- NSLog(@"%@", error);
- }
- }
- -(void)hideKeyboard:(id)sender
- {
- [self.textField resignFirstResponder];
- }
Run一下:
控制台输出:
- 2013-08-27 15:02:02.416 SimpleCoreData_Demo[22382:a0b] Saving succeed
- 2013-08-27 15:02:23.157 SimpleCoreData_Demo[22382:a0b] Saving succeed
- 2013-08-27 15:02:38.511 SimpleCoreData_Demo[22382:a0b] Saving succeed
- 2013-08-27 15:03:00.213 SimpleCoreData_Demo[22382:a0b] Saving succeed
- 2013-08-27 15:03:06.096 SimpleCoreData_Demo[22382:a0b] Saving succeed
- 2013-08-27 15:03:10.746 SimpleCoreData_Demo[22382:a0b] Saving succeed
- 2013-08-27 15:03:12.460 SimpleCoreData_Demo[22382:a0b] title:title3
- 2013-08-27 15:03:12.461 SimpleCoreData_Demo[22382:a0b] title:title2
- 2013-08-27 15:03:12.461 SimpleCoreData_Demo[22382:a0b] title:title2
- 2013-08-27 15:03:12.462 SimpleCoreData_Demo[22382:a0b] title:title1
- 2013-08-27 15:03:12.464 SimpleCoreData_Demo[22382:a0b] title:title1
- 2013-08-27 15:03:12.465 SimpleCoreData_Demo[22382:a0b] title:simple6
- 2013-08-27 15:03:12.466 SimpleCoreData_Demo[22382:a0b] title:simple5
- 2013-08-27 15:03:12.466 SimpleCoreData_Demo[22382:a0b] title:simple4
(二)CodingCoreData_Demo:存取经过编码后的复杂数据类型
对于像NSArray和NSDictionary或者是一个类这样复杂的数据,如果要对其进行存取,则先要将其编码成NSData数据。其实任何数据都可以转化成NSData数据,然后再对其进行存取。
在本例中创建了一个名为BookData的Entity,其中BookData包含一个成员book,类型为NSData,用于保存一个Book类对象,该类有一个字典成员info。
由于要对Book类对象进行存取,所以该类必须遵守NSCoding协议并实现required方法:
- #import <Foundation/Foundation.h>
- @interface Book : NSObject <NSCoding> // 必须遵守NSCoding协议
- @property (retain, nonatomic) NSMutableDictionary *info; // 属性字典
- @end
- #import "Book.h"
- @implementation Book
- // 必须实现initWithCoder:方法
- -(id)initWithCoder:(NSCoder *)aDecoder
- {
- self = [super init];
- if (self) {
- self.info = [aDecoder decodeObjectForKey:@"info"]; // 解码
- }
- return self;
- }
- // 必须实现encodeWithCoder:方法
- -(void)encodeWithCoder:(NSCoder *)aCoder
- {
- [aCoder encodeObject:self.info forKey:@"info"]; // 编码
- }
- @end
下面是添加数据,抓取数据和删除所有存储好的数据的方法:
- @implementation ViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- self.myDelegate = [[UIApplication sharedApplication] delegate]; // 获取委托类
- }
- - (void)didReceiveMemoryWarning
- {
- [super didReceiveMemoryWarning];
- // Dispose of any resources that can be recreated.
- }
- // 添加数据
- - (IBAction)addData:(id)sender {
- // 从托管对象上下文中获取BookData名对应的Entity对象
- NSManagedObjectContext *context = [self.myDelegate managedObjectContext];
- NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"BookData" inManagedObjectContext:context];
- BookData *bd = [[BookData alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:context];
- // 设置要保存的数据
- Book *book = [[Book alloc] init];
- NSDictionary *dic = @{@"name": self.name_textField.text,
- @"charsPerPage": self.cpp_textField.text
- };
- book.info = [dic mutableCopy]; // 设置book的字典info
- NSData *data = [NSKeyedArchiver archivedDataWithRootObject:book]; // 对book对象进行归档
- bd.book = data; // 设置bd的book对象(NSData类型)
- // 保存数据到托管对象上下文中
- NSError *error = nil;
- BOOL isSaveSuccess = [context save:&error];
- if (isSaveSuccess) {
- NSLog(@"Save succeed");
- }
- else {
- NSLog(@"%@", error);
- }
- }
- // 抓取数据
- - (IBAction)fetchData:(id)sender {
- // 设置抓取请求的对象为名为BookData对应的Entity对象
- NSManagedObjectContext *moc = [self.myDelegate managedObjectContext];
- NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"BookData" inManagedObjectContext:moc];
- NSFetchRequest *request = [[NSFetchRequest alloc] init];
- [request setEntity:entityDescription];
- // 抓取数据,返回结果为NSArray类型数据
- NSError *error = nil;
- NSArray *array = [NSMutableArray arrayWithArray:[moc executeFetchRequest:request error:&error]];
- if (array == nil || error != nil) {
- NSLog(@"Fetch result is nil");
- }
- else {
- for (BookData *bd in array) {
- Book *book = [NSKeyedUnarchiver unarchiveObjectWithData:bd.book]; // 对bd的book数据(NSData类型)解压
- NSLog(@"%@", book.info); // 输出字典信息
- }
- }
- }
- // 删除所有保存的数据
- - (IBAction)deleteData:(id)sender {
- /* 方法一:先抓取数据,然后遍历数组将其数据清空
- NSManagedObjectContext *moc = [self.myDelegate managedObjectContext];
- NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"BookData" inManagedObjectContext:moc];
- NSFetchRequest *request = [[NSFetchRequest alloc] init];
- [request setEntity:entityDescription];
- NSError *error = nil;
- NSArray *array = [NSMutableArray arrayWithArray:[moc executeFetchRequest:request error:&error]];
- if (array == nil) {
- return;
- }
- else {
- for (NSManagedObject *item in array) {
- [moc deleteObject:item];
- }
- }
- [moc save:&error];
- if (!error) {
- NSLog(@"Delete succeed");
- }
- else {
- NSLog(@"%@", error);
- }
- */
- // 方法二:在持久化存储区中移除存储数据的文件
- //Erase the persistent store from coordinator and also file manager.
- NSPersistentStore *store = [self.myDelegate.persistentStoreCoordinator.persistentStores lastObject];
- NSError *error = nil;
- NSURL *storeURL = store.URL;
- [self.myDelegate.persistentStoreCoordinator removePersistentStore:store error:&error];
- [[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
- NSLog(@"Delete succeed");
- //Make new persistent store for future saves (Taken From Above Answer)
- if (![self.myDelegate.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
- // do something with the error
- NSLog(@"Delete error");
- }
- }
- @end
Run一下:
控制台输出:
- 2013-08-27 21:46:43.074 CodingCoreData_Demo[670:a0b] Save succeed
- 2013-08-27 21:47:04.685 CodingCoreData_Demo[670:a0b] Save succeed
- 2013-08-27 21:47:24.245 CodingCoreData_Demo[670:a0b] Save succeed
- 2013-08-27 21:47:28.671 CodingCoreData_Demo[670:a0b] {
- charsPerPage = 1;
- name = "a.txt";
- }
- 2013-08-27 21:47:28.675 CodingCoreData_Demo[670:a0b] {
- charsPerPage = 2;
- name = "b.rtf";
- }
- 2013-08-27 21:47:28.677 CodingCoreData_Demo[670:a0b] {
- charsPerPage = 3;
- name = "c.pdf";
- }
- 2013-08-27 21:47:33.569 CodingCoreData_Demo[670:a0b] Delete succeed
- 2013-08-27 21:47:47.095 CodingCoreData_Demo[670:a0b] Save succeed
- 2013-08-27 21:48:00.653 CodingCoreData_Demo[670:a0b] Save succeed
- 2013-08-27 21:48:07.565 CodingCoreData_Demo[670:a0b] Save succeed
- 2013-08-27 21:48:09.471 CodingCoreData_Demo[670:a0b] {
- charsPerPage = 100;
- name = "book.txt";
- }
- 2013-08-27 21:48:09.472 CodingCoreData_Demo[670:a0b] {
- charsPerPage = 100;
- name = "book.rtf";
- }
- 2013-08-27 21:48:09.474 CodingCoreData_Demo[670:a0b] {
- charsPerPage = 100;
- name = "book.pdf";
- }
以上是Core Data存取数据的简单使用方法。