도움 받은 곳 : http://natashatherobot.com/ios-core-data-singleton-example/
db가 필요한 예제 만들다가 sqlite 클래스 만들기 귀찮아서 이 참에 core data를 써봐야겠다 싶어서 이 예제를 만들었다.
#import <Foundation/Foundation.h> @interface DBManager : NSObject + (DBManager*) sharedInstance; - (NSFetchedResultsController*) fetchAllWithClassName:(NSString*)className cacheName:(NSString*)cacheName; - (NSFetchedResultsController *)fetchEntitiesWithClassName:(NSString *)className sortDescriptors:(NSArray *)sortDescriptors sectionNameKeyPath:(NSString *)sectionNameKeypath predicate:(NSPredicate *)predicate; - (void)saveWithBlock:(void (^)(BOOL saved, NSError *error))savedBlock; - (void)deleteEntity:(NSManagedObject *)entity; - (id)createEntityWithClassName:(NSString *)className attributesDictionary:(NSDictionary *)attributesDictionary; @end
#import "DBManager.h" @interface DBManager() @property (strong, strong, nonatomic) NSManagedObjectContext *managedObjectContext; @property (strong, strong, nonatomic) NSManagedObjectModel *managedObjectModel; @property (strong, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; @end @implementation DBManager static DBManager *_instance; +(DBManager*) sharedInstance { if (!_instance) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [DBManager new]; }); } return _instance; } -(id) init { self = [super init]; if (self) { [self connect]; } return self; } -(void)connect { NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"TestCoreData" withExtension:@"momd"]; self.managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel]; NSURL *documentsDirectory =[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; NSURL *storeURL = [documentsDirectory URLByAppendingPathComponent:@"TestCoreData3.sqlite"]; NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES],NSInferMappingModelAutomaticallyOption, nil]; NSError *error = nil; if (![self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) { NSLog(@"===============\nUnresolved error %@, %@", error, [error userInfo]); abort(); } else { self.managedObjectContext = [NSManagedObjectContext new]; [self.managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator]; } } - (NSFetchedResultsController*) fetchAllWithClassName:(NSString*)className cacheName:(NSString*)cacheName { NSFetchRequest *fetchRequest = [NSFetchRequest new]; // Edit the entity name as appropriate. NSEntityDescription *entity = [NSEntityDescription entityForName:className inManagedObjectContext:self.managedObjectContext]; [fetchRequest setEntity:entity]; [fetchRequest setFetchBatchSize:20]; NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"createdAt" ascending:NO]; NSArray *sortDescriptors = @[sortDescriptor]; [fetchRequest setSortDescriptors:sortDescriptors]; NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:cacheName]; NSError *error = nil; if (![fetchedResultsController performFetch:&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(); } return fetchedResultsController; } - (NSFetchedResultsController *)fetchEntitiesWithClassName:(NSString *)className sortDescriptors:(NSArray *)sortDescriptors sectionNameKeyPath:(NSString *)sectionNameKeypath predicate:(NSPredicate *)predicate { NSFetchedResultsController *fetchedResultsController; NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:className inManagedObjectContext:self.managedObjectContext]; fetchRequest.entity = entity; fetchRequest.sortDescriptors = sortDescriptors; fetchRequest.predicate = predicate; fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:sectionNameKeypath cacheName:nil]; NSError *error = nil; BOOL success = [fetchedResultsController performFetch:&error]; if (!success) { NSLog(@"fetchManagedObjectsWithClassName ERROR: %@", error.description); } return fetchedResultsController; } - (id)createEntityWithClassName:(NSString *)className attributesDictionary:(NSDictionary *)attributesDictionary { NSManagedObject *entity = [NSEntityDescription insertNewObjectForEntityForName:className inManagedObjectContext:self.managedObjectContext]; NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:attributesDictionary]; [dictionary setValue:[NSDate date] forKey:@"createdAt"]; [dictionary setValue:[NSDate date] forKey:@"updatedAt"]; [dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { [entity setValue:obj forKey:key]; }]; return entity; } - (void)saveWithBlock:(void (^)(BOOL saved, NSError *error))savedBlock { NSError *saveError = nil; savedBlock([self.managedObjectContext save:&saveError], saveError); } - (void)deleteEntity:(NSManagedObject *)entity { [self.managedObjectContext deleteObject:entity]; } @end
일단 이렇게 하면 어느 정도 필요한 건 다 된 것 같다.
그냥 sqlite 랑 비슷하다고만 생각했는데, 개념이 많이 달라서 고생 좀 했다 ㅠㅠ
보통의 db는 select하고, insert하는 걸 바로바로 해줘야하는데, 얘는 그런거 없이 배열 정보로 갖고 있다가 한방에 저장시켜 버린다.
그래서 core data 예제를 만들면 앱이 꺼질 때 딱 한 번 save를 한다….
이거 때문에 아주 많이 헤맸다.
근데 이거 테이블 많아지면서, 관계설정 들어가기 시작하면 감당 안되지 싶은데 ;;;;;
// fetch all //self.fetchedResultsController = [self.dbManager fetchAllWithClassName:@"Event" cacheName:@"Master"]; // fetch with sort NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES]; NSArray *sortDescriptors = @[sortDescriptor]; self.fetchedResultsController = [[DBManager sharedInstance] fetchEntitiesWithClassName:@"Event" sortDescriptors:sortDescriptors sectionNameKeyPath:nil predicate:nil]; self.fetchedResultsController.delegate = self; // count NSLog(@"count : %d", self.fetchedResultsController.fetchedObjects.count); // get one NSManagedObject *object = [self.fetchedResultsController.fetchedObjects objectAtIndex:0]; NSLog(@"info : %@", [object valueForKey:@"name"]);
select 는 이렇게 하면 된다.
where 절에 해당하는 predicate는 NSPredicate 로 검색하는게 나을 것 같다.
보통의 where와는 많이 달라서 테스트를 해보는게 좋을 듯…..
NSManagedObject *object = [self.fetchedResultsController.fetchedObjects objectAtIndex:0]; [object setValue:self.nameField.text forKey:@"name"];
fetchedResultsController에서 NSManagerObject를 가져오고, 그거의 특정 key에 해당하는 value를 바꿔주면 된다.
NSDictionary와 사용법은 같다고 생각하면 이해하기 편하다.
NSMutableDictionary *dictionary = [NSMutableDictionary new]; [dictionary setValue:self.nameField.text forKey:@"name"]; [[DBManager sharedInstance] createEntityWithClassName:@"Event" attributesDictionary:dictionary];
Event 모델의 name에 값을 넣어서 배열에 저장하는 부분이다.
보통의 db였다면 이 부분에서 insert 쿼리가 실행됐을 테지만, core data는 그런거 없다.
[[DBManager sharedInstance] saveWithBlock:^(BOOL saved, NSError *error) { if (!saved) NSLog(@"error : %@", error); else [self.navigationController popViewControllerAnimated:YES]; }];
실제로는 이런 식으로 save를 날려줘야 저장된다.
이러면 fetchedResults 에 변경된 놈, 추가된 놈, 수정된 놈들의 쿼리가 날아간다.
특히 이 부분 때문에 이해하기 정말 힘들었다.
전체 소스는 https://github.com/susemi99/TestCoreData 에 올려뒀다.