最近做聊天記錄緩存的東西,用到了數據庫的.本想著用FMDB因為之前用過,回來看了看Core Data也是不錯的,今天想把遇到的坑記錄一下.
- 首先說,為什么用到數據庫?
如果只做數據緩存,那么用歸檔或者將Json寫到文件就可以了.若用到了增刪改查,那么就要用到數據庫了. -
準備工作:
1.創建一個文件
373868DF-90CF-4065-921A-EF5A4B0425EF.png
注意:每個工程的Data Model 文件必須新建一個,新建該文件會和該工程關聯,如果只是拖入了一個該文件會報錯.
2.建立數據表
屏幕快照 2016-04-11 下午3.35.46.png
- Add Entity添加實體 ->相當于Advice_Flag對象
- Add Attribute添加屬性 ->name...屬性
注意:要有有key1,key2... 這個為保留字段防止以后加入字段.因為如果和本地的數據庫表不一致的話,會引起程序的崩潰.
- 3.數據庫存的對象是NSManagedObject,所以你寫入數據庫的是對象是Student,根據你創建Entity,選中Student實體
屏幕快照 2016-03-09 上午10.45.30.png
生成對應的文件
屏幕快照 2016-04-11 下午4.22.31.png
你會發現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
內容結構如下:
屏幕快照 2016-04-11 下午4.29.02.png
- 注意實體的屬性名(Student的name字段)最好合后臺的字段一樣.因為這樣可以把數據庫里的字段拼成后臺返回的數據格式再次解析成你能用的Model.
具體如代碼注釋,基類已經創建好了.那么我們還需要一個管理類Core Data Manager,負責的增刪改查.
假設你的項目使用的是StudentModel,數據庫存儲的model 是Student StudentModel字段最好跟Student字段名一致,不一致需要為Student字段賦值,如圖:
屏幕快照 2016-04-11 下午4.48.16.png
你需要將你的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接收函數的返回對象,不然代理函數不會走.