iOS学习37数据处理之CoreData
1. CoreData数据库框架的优势
1> CoreData历史
CoreData数据持久化框架是Cocoa API 的一部分,首次在iOS5版本的系统中出现,它允许按照实体-属性-值模型组织数据,并以XML、二级制文件或者SQLite数据文件的格式持久化数据。
CoreData主要提供对象 --- 关系映射(ORM)功能,把OC对象转化为数据保存到文件,也可以数据转化为OC对象。
2> CoreData数据库与Sqlite数据库的比较
-
基于C接口,需要使用sql语句,但是代码较为繁琐
-
在处理大量数据时,表关系更为直观
-
在OC中不是可视化的
-
可以跨平台使用(iOS和安卓)
CoreData:
-
可视化,有undo/redo能力
-
可以实现多种文件格式NSSQLiteStoreType、NSBinaryStoreType、NSInMemoryStoreType、NSXMLStoreType
-
苹果官方API支持,与iOS结合更紧密
-
不能跨平台使用,只支持iOS
3> CoreData核心对象及关系实例(以一个餐馆为例)
2. CoreData数据库框架的核心对象
1> 被管理对象上下文(NSManagedObjectContext)
NSManagedObjectContext:被管理对象上下文,CoreData中用于操作和使用数据,负责应用和数据库之间的交互,可以进行增、删、改、查
数据的保存需要 NSManagedObjectContext 进行 save 操作
数据的查询需要 NSManagedObjectContext 进行 executeFetchRequest 操作(返回值是数组)
CoreData提供的是对象关系映射,NSManagedObjectContext操作的都是NSManagedObject对象
2> 被管理对象相关类
NSManagedObjectModel:被管理对象模型,管理多个对象
NSManagedObject:被管理对象,CoreData返回的数据类型,被管理的对象是根据是根据实体描述生成的
NSEntityDescription:实体描述类,根据实体创建被管理对象,可以映射出对象
Entity:实体类,实体是对文件数据的描述。被管理对象表示实体,实体包含名称,属性(字段)和关系,实体的名称通常和被管理对象名一致
3> 持久化存储和存储文件
数据连接器类:NSPersistentStoreCoordinator,持久化存储调节器,它想要去SQLite数据库中拿数据必须通过NSPersistentStore。
NSPersistentStore:持久化存储,是对实际文件的一种。Objective-C表示方式,一个被封装好的底层类,用于存储数据
存储文件:用来存储和管理数据的文件,iOS支持4种存储类型:NSSQLiteStoreType、NSBinaryStoreType、NSInMemoryStoreType、NSXMLStoreType
4> 数据查询
NSFetchRequest:查询请求,可以做排序操作,也可以使用谓词
NSManagedObjectModel根据NSFetchRequest查询数据,以数组形式返回,数组中包含被管理对象(NSManagedObject)
NSSortDescriptor:排序操作
3. CoreData数据库的简单操作
1> 创建步骤
① 创建带有CoreData数据库的工程
② 创建实体类与属性
③ 切换可视化关系图
④ 创建实例类
2> CoreData上下文(NSManagedObjectContext)的创建
创建NSManagedObjectContext 这个类的对象 一般都用从AppDelegate里面的拿过来用,不用单独创建,创建工程勾选 use CoreData 这些代码AppDelegate就创建好
1 @interface ViewController () 2 3 #pragma mark - 第二步:声明属性【声明管理对象上下文】(Sqlite中是声明一个存储数据路径的属性) 4 @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext; 5 6 @end 7 8 @implementation ViewController 9 10 #pragma mark - 懒加载(与以前有所区别) 11 - (NSManagedObjectContext *)managedObjectContext 12 { 13 // 因为在Appdelegate中已经实现过了,所以这里是从Appdelegate中去获取 14 if (!_managedObjectContext) { 15 16 // 获取appDelegate对象,使用系统的单例方法创建 17 AppDelegate *appDelegate = [UIApplication sharedApplication].delegate; 18 19 _managedObjectContext = appDelegate.managedObjectContext; 20 } 21 return _managedObjectContext; 22 } 23 24 @end
3> AppDelegate创建好的代码解析(详解请看注释)
AppDelegate.h
1 #import <UIKit/UIKit.h> 2 #import <CoreData/CoreData.h> 3 4 @interface AppDelegate : UIResponder <UIApplicationDelegate> 5 6 @property (strong, nonatomic) UIWindow *window; 7 8 /// 管理数据库上下文的对象,可以进行增、删、改、查 9 @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext; 10 11 /// 管理数据库中的对象(对象模型),可以将工程中的对象合并(为了更加方便的生成数据库中的表) 12 @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; 13 14 /// 数据库持久化协调器,实际的工作都是由它来做的 15 @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; 16 17 - (void)saveContext; 18 - (NSURL *)applicationDocumentsDirectory; 19 20 @end
AppDelegate.m
1 #import "AppDelegate.h" 2 3 @interface AppDelegate () 4 5 @end 6 7 @implementation AppDelegate 8 9 #pragma mark - Core Data stack 10 11 @synthesize managedObjectContext = _managedObjectContext; 12 @synthesize managedObjectModel = _managedObjectModel; 13 @synthesize persistentStoreCoordinator = _persistentStoreCoordinator; 14 15 // 返回coreData存储的路径 16 - (NSURL *)applicationDocumentsDirectory { 17 18 // 5. 打印coreData存储的路径【Sqlite的dbPath字符串路径】 19 NSLog(@"filePath = %@", [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]); 20 21 // 返回coreData存储的路径 22 return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; 23 } 24 25 #pragma mark - 获取管理对象 26 - (NSManagedObjectModel *)managedObjectModel { 27 28 if (_managedObjectModel != nil) { 29 return _managedObjectModel; 30 } 31 32 // 从应用程序中加载模型文件 33 // momd 是编译后的文件后缀 34 NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"UISenior_4_1_CoreData" withExtension:@"momd"]; 35 36 _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];// 代表的就是刚刚创建的实体 37 38 return _managedObjectModel; 39 } 40 41 #pragma mark - 数据持久化协调器(真正干活的) 42 // persistentStoreCoordinator的getter方法 43 - (NSPersistentStoreCoordinator *)persistentStoreCoordinator { 44 45 if (_persistentStoreCoordinator != nil) { 46 return _persistentStoreCoordinator; 47 } 48 49 // 连接器对象关联的实体模型 50 _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; 51 52 // 定义数据存储的路径 53 NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"UISenior_4_1_CoreData.sqlite"]; 54 55 NSError *error = nil; 56 NSString *failureReason = @"There was an error creating or loading the application's saved data."; 57 58 // NSSQLiteStoreType这个参数决定文件存储的形式(sqlite、XML、二进制等) 59 if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { 60 // 返回可能出现的错误信息 61 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 62 dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data"; 63 dict[NSLocalizedFailureReasonErrorKey] = failureReason; 64 dict[NSUnderlyingErrorKey] = error; 65 error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict]; 66 67 // abort()会导致应用程序生成一个崩溃日志和终止。虽然它可能是有用的在开发过程中, 但是你不应该使用这个函数在运行的应用程序中。 68 NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 69 abort(); 70 } 71 72 return _persistentStoreCoordinator; 73 } 74 75 #pragma mark - 获取数据库上下文 76 // managedObjectContext的getter方法 77 - (NSManagedObjectContext *)managedObjectContext { 78 79 if (_managedObjectContext != nil) { 80 return _managedObjectContext; 81 } 82 83 // 创建数据持久化协调器 84 NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; 85 86 if (!coordinator) { 87 return nil; 88 } 89 90 // 创建managedObjectContext对象 91 // ConcurrencyType 并发类型,是一个枚举值 92 // NSMainQueueConcurrencyType 主队列并发类型(2) 93 // NSPrivateQueueConcurrencyType 私有队列并发类型(1) 94 // NSConfinementConcurrencyType 限制并发类型(0) 95 _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; 96 97 // 数据连接器 关联 被管理上下文 98 [_managedObjectContext setPersistentStoreCoordinator:coordinator]; 99 100 return _managedObjectContext; 101 } 102 103 #pragma mark - Core Data Saving support 104 105 - (void)saveContext { 106 NSManagedObjectContext *managedObjectContext = self.managedObjectContext; 107 if (managedObjectContext != nil) { 108 // 同上面的错误处理 109 NSError *error = nil; 110 if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { 111 112 NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 113 abort(); 114 } 115 } 116 } 117 118 @end
4> 添加对象(详解请看注释)
1 - (void)addPerson 2 { 3 // 添加的步骤 4 // 1. 创建Person实体对象,然后开始"context",让它做好准备,将这个对象添加到数据库 5 6 /** 7 * 实例对象有两种[初始化实体对象需要借用NSEntityDescription] 8 */ 9 10 // 第一种: 11 // Person *per = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.managedObjectContext]; 12 // 第二种: 13 // 先创建一个实体 14 NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:self.managedObjectContext]; 15 // 创建Person对象 16 Person *per = [[Person alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:self.managedObjectContext]; 17 18 // 2. 给对象属性赋值 19 per.personName = @"MBBoy"; 20 per.personGender = @"Unkown"; 21 per.personAge = @10; 22 23 // 3. 将对象存入数据库 24 BOOL result = [_managedObjectContext save:nil]; 25 26 per.personCar = carSet; 27 28 // 4. 判断是否插入成功(在AppDelegate中打印地址) 29 if (result) { 30 NSLog(@"添加数据成功"); 31 } else { 32 NSLog(@"添加数据失败"); 33 } 34 }
5> 删除对象(详解请看注释)
1 - (void)deletePerson 2 { 3 // 1. 实体化请求类【查询】 4 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"]; 5 6 // 2. 获取删除的条件(NSPredicate) 7 request.predicate = [NSPredicate predicateWithFormat:@"personName = 'MBBoy'"]; 8 9 // 3. 由context根据删除条件的请求去具体进行删除操作 10 NSArray *resultArray = [self.managedObjectContext executeFetchRequest:request error:nil]; 11 12 // 为了代码的严密需要判断resultArray中是否有值 13 if (0 == resultArray.count) { 14 NSLog(@"未找到删除对象"); 15 } else { 16 // 4. 遍历搜索出来结果 17 for (Person *per in resultArray) { 18 // 删除查询到相关的人的信息 19 [self.managedObjectContext deleteObject:per]; 20 } 21 22 // 5. 进行删除结果的判断,保存后删除操作才会写入文件 23 BOOL result = [_managedObjectContext save:nil]; 24 if (result) { 25 NSLog(@"删除数据成功"); 26 } else { 27 NSLog(@"删除数据失败"); 28 } 29 } 30 }
6> 更改对象(详解请看注释)
1 - (void)updatePerson 2 { 3 // 1. 实体化请求类【查询】 4 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"]; 5 6 // 2. 设置查询的条件 7 request.predicate = [NSPredicate predicateWithFormat:@"personName = 'MBBoy'"]; 8 9 // 3. 由context根据查询条件的请求去具体进行更新操作 10 NSArray *resultArray = [self.managedObjectContext executeFetchRequest:request error:nil]; 11 12 // 为了代码的严密需要判断resultArray中是否有值 13 if (0 == resultArray.count) { 14 NSLog(@"未找到更改对象,请检查谓词条件!"); 15 } else { 16 // 4. 遍历搜索结果 17 for (Person *per in resultArray) { 18 19 // 更新查询到相关的人的信息 20 per.personName = @"小强"; 21 per.personGender = @"卵男"; 22 per.personAge = @38; 23 } 24 25 // 5. 进行删除结果的判断 26 BOOL result = [_managedObjectContext save:nil]; 27 if (result) { 28 NSLog(@"更改数据成功"); 29 } else { 30 NSLog(@"更改数据失败"); 31 } 32 } 33 }
7> 查询对象(详解请看注释)
1 - (void)selectPerson 2 { 3 // 1. 实体化请求类【查询】 4 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"]; 5 6 // 2. 设置查询的条件 7 request.predicate = [NSPredicate predicateWithFormat:@"personName like '*B*'"]; 8 9 // 3. 根据管理对象上下文执行相关的操作 10 NSArray *resultArray = [self.managedObjectContext executeFetchRequest:request error:nil]; 11 12 if (0 == resultArray.count) { 13 NSLog(@"未找到查询对象,请检查谓词条件!"); 14 } else { 15 for (Person *per in resultArray) { 16 NSLog(@"name = %@, gender = %@, age = %@", per.personName, per.personGender, per.personAge); 17 } 18 } 19 }
补: 查询结果的排序
1 // 设置排序方式 2 NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"address" ascending:NO]; 3 request.sortDescriptors = @[sort];
4. CoreData数据库表关联操作
1> 添加表关系可视化操作
Car数据库与Person数据库建立关联:
Person数据库与Car数据库建立关联:
2> 添加表关系代码操作
1 // 创建Person对象 2 Person *per = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.managedObjectContext]; 3 // 2. 给对象属性赋值 4 per.personName = @"MBBoy"; 5 per.personGender = @"Unkown"; 6 per.personAge = @10; 7 8 ///// 将车的对象通过实体描述类创建出来 9 Car *audiCar = [NSEntityDescription insertNewObjectForEntityForName:@"Car" inManagedObjectContext:self.managedObjectContext]; 10 11 audiCar.price = @300000; 12 audiCar.color = @"白色"; 13 audiCar.brand = @"奥迪A7"; 14 15 Car *benchiCar = [NSEntityDescription insertNewObjectForEntityForName:@"Car" inManagedObjectContext:self.managedObjectContext]; 16 17 benchiCar.price = @500000; 18 benchiCar.color = @"红色"; 19 benchiCar.brand = @"奔驰SUV"; 20 21 // 将两辆车放到集合中存储,然后进行赋值 22 NSSet *carSet = [NSSet setWithObjects:audiCar, benchiCar, nil]; 23 24 // 3. 将对象存入数据库 25 BOOL result = [_managedObjectContext save:nil]; 26 27 // 建立一对多关系 28 per.personCar = carSet; 29 30 // 4. 判断是否插入成功(在AppDelegate中打印地址) 31 if (result) { 32 NSLog(@"添加数据成功"); 33 } else { 34 NSLog(@"添加数据失败"); 35 }
补: 一对多表关系时CoreData自动生成基本操作方法
1 // 添加人与一辆车之间的关系 2 - (void)addPersonCarObject:(NSManagedObject *)value; 3 // 删除人与一辆车之间的关系 4 - (void)removePersonCarObject:(NSManagedObject *)value; 5 // 添加人与一组车之间关系 6 - (void)addPersonCar:(NSSet<NSManagedObject *> *)values; 7 // 删除人与一组车之间关系 8 - (void)removePersonCar:(NSSet<NSManagedObject *> *)values;
注:此处只是表间关系删除,并不会删除文件内数据
5. CoreData数据库数据的迁移
1> 概述
CoreData 支持随着App开发演进而带来的对象模型(NSManagedObjectModel)升级或修改的管理。模型的改变将导致不兼容(或不能打开)以前版本创建的存储。如果你要改变你的模型,你就必须要改变现有存储中的数据 - 即数据存储格式(store format)——这被称为数据迁移(migration)
2> 数据迁移的三个阶段
-
创建基于源实例对象的目标实例对象;
-
重新建立联系;
-
验证与保存;