最近做聊天记录缓存的东西,用到了数据库的.本想着用FMDB因为之前用过,回来看了看Core Data也是不错的,今天想把遇到的坑记录一下.
- 首先说,为什么用到数据库?
如果只做数据缓存,那么用归档或者将Json写到文件就可以了.若用到了增删改查,那么就要用到数据库了. -
准备工作:
1.创建一个文件
注意:每个工程的Data Model 文件必须新建一个,新建该文件会和该工程关联,如果只是拖入了一个该文件会报错.
2.建立数据表
- Add Entity添加实体 ->相当于Advice_Flag对象
- Add Attribute添加属性 ->name...属性
注意:要有有key1,key2... 这个为保留字段防止以后加入字段.因为如果和本地的数据库表不一致的话,会引起程序的崩溃.
- 3.数据库存的对象是NSManagedObject,所以你写入数据库的是对象是Student,根据你创建Entity,选中Student实体
生成对应的文件
你会发现Student是继承于NSManagedObject的子类,现在存数据库的Model已经准备好了
- 那么我们开始创建数据库代码如下:
@interface CoreDataDAO : NSObject
//被管理的对象上下文
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
//被管理的对象模型
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
//持久化存储协调者
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (NSURL *)applicationDocumentsDirectory;
- (void)saveContext;
- (NSManagedObjectContext*)managedObjectContext;
- (NSPersistentStoreCoordinator*)persistentStoreCoordinator;
- (NSManagedObjectModel*)managedObjectModel;
@end
#import "CoreDataDAO.h"
@implementation CoreDataDAO
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
//返回 被管理的对象上下文
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator) {
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
// 返回 持久化存储协调者
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator) {
return _persistentStoreCoordinator;
}
//数据库文件
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"NHDoctor.sqlite"];
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
[_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:nil
error:nil];
return _persistentStoreCoordinator;
}
// 返回 被管理的对象模型
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel) {
return _managedObjectModel;
}
//模型文件
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"你的文件名" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
#pragma mark - 应用程序沙箱
// 返回应用程序Docment目录的NSURL类型
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSError *error = nil;
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
@end
内容结构如下:
- 注意实体的属性名(Student的name字段)最好合后台的字段一样.因为这样可以把数据库里的字段拼成后台返回的数据格式再次解析成你能用的Model.
具体如代码注释,基类已经创建好了.那么我们还需要一个管理类Core Data Manager,负责的增删改查.
假设你的项目使用的是StudentModel,数据库存储的model 是Student StudentModel字段最好跟Student字段名一致,不一致需要为Student字段赋值,如图:
你需要将你的StudentModel 转换成 Student类的Model, Student用KVC赋值:
- (void)setValue:(nullable id)value forKey:(NSString *)key;
- 插入数据
- (BOOL)insertData:(id)dataModel{
//配置项(文件名)
CoreDataConfig *config = [CoreDataConfig defaulter];
//config.tableName 表名
//[self managedObjectContext] 上下文,都在配置项里配置好了
Student *student= [NSEntityDescription insertNewObjectForEntityForName:config.tableName inManagedObjectContext:[self managedObjectContext]];
//对student插入内容赋值
[self addDataWithModel:student withDic:dataModel];
//保存
return [self saveContext];
}
用的时候导入
#import <objc/runtime.h>
- (void)addDataWithModel:(id)sourceData withDic:(id)incomingData{
u_int count;
objc_property_t *properties=class_copyPropertyList([sourceData class], &count);
for (int i = 0; i < count ; i++)
{
const char* propertyName =property_getName(properties[i]);
NSString *strName = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
if ([incomingData valueForKey:strName] != nil) {
[sourceData setValue:[incomingData valueForKey:strName] forKey:strName];
}
}
}
这样插入就做好了(匠心),再去沙盒去看看数据,字段改变一定要删除沙盒的数据库要不会崩溃.
- 删除 代码如下:
-(BOOL)deleteData:(id)dataModel{
CoreDataConfig *config = [CoreDataConfig defaulter];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:config.tableName inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"name = %@", ((Student*)dataModel).name];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *listData = [self.managedObjectContext executeFetchRequest:request error:&error];
BOOL is = NO;
for (id data in listData) {
[self.managedObjectContext deleteObject:data];
[self saveContext];
is = YES;
}
return is;
}
- 改
-(BOOL)updataData:(id)dataModel{
CoreDataConfig *config = [CoreDataConfig defaulter];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:config.tableName inManagedObjectContext:self.managedObjectContext]];
NSPredicate* predicate = nil;
//查询关键字,关键字studentId 不能用%@代替,必须写出来
predicate = [NSPredicate predicateWithFormat:@"studentId = %@",((Student*)dataModel).studentId];
[fetchRequest setPredicate:predicate];
NSError* error = nil;
NSArray* arr = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (Student* data in arr) {
[self addDataWithModel:data withDic:dataModel];
}
//保存
return [self saveContext];
}
- 查
-(NSArray*)query:(id)dataModel{
CoreDataConfig *config = [CoreDataConfig defaulter];
NSEntityDescription* entity = [NSEntityDescription entityForName:config.tableName inManagedObjectContext:self.managedObjectContext];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
//组合谓词用AND ,studentId = %@ AND name = %@ ,'=' 可以换成< 等符号
NSPredicate* predicate;
predicate = [NSPredicate predicateWithFormat:@"studentId = %@",((Student*)dataModel).studentId];
[fetchRequest setPredicate:predicate];
//排序
NSSortDescriptor* sort = [NSSortDescriptor sortDescriptorWithKey:@"studentId" ascending:NO];
[fetchRequest setSortDescriptors:@[sort]];
//查询个数
[fetchRequest setFetchLimit:10];
NSError* error = nil;
NSArray* arr = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
return arr;
}
到此增删改查已经写好,还有一个重要的一点,增删改查你得知道什么时候发生的,所以必须得有一个数据改动的回调函数,苹果帮我们写好了这个函数 NSFetchedResultsController
-(NSFetchedResultsController*)creatFetchedResultWithController:(id)delegate withDic:(id)dataModel{
CoreDataConfig *config = [CoreDataConfig defaulter];
NSFetchRequest* request = [[NSFetchRequest alloc] init];
NSEntityDescription* entity = [NSEntityDescription entityForName:config.tableName inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"name = %@", ((Student*)dataModel).name]; [request setPredicate:predicate];
//排序必须写的
NSSortDescriptor* sort = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
[request setSortDescriptors:@[sort]];
NSFetchedResultsController* controller = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
controller.delegate = delegate;
NSError* error = nil;
if ([controller performFetch:&error]) {
}
//返回的 controller 一定要有持有者
return controller;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
StudentModel *student = [[StudentModel alloc] init];
student.name = @"小明";
self.fetchedController = [[CoreDataManager sharedManager] creatFetchedResultWithController:self withDic:student];
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(nullable NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(nullable NSIndexPath *)newIndexPath{
}
- 注意一定要self.fetchedController接收函数的返回对象,不然代理函数不会走.