iOS中 CoreData 的使用介绍

来源:互联网 发布:用淘宝助理复制宝贝 编辑:程序博客网 时间:2024/05/01 03:30

前言:

Core data是苹果官方提供的一套框架,主要用来解决与对象生命周期管理,对象关系图管理和持久化的等方面的问题。

为什么要使用Core data?

1. 有丰富且良好的文档,方便使用。
2. 有着很多经过开发者检验的代码。
3. 苹果出品使得它与OS X 和iOS开发工具链密切结合,我们可以在Xcode上进行表的设计,或者在instruments上进行性能检测。并且在开发过程中能够很自然地维持这cocoa的开发风格。
4. 本身所具有的特性,比如支持多类型外部存储,撤销/重做、KVC、复杂查询和对象映射、自动验证、并发/合并策略、数据迁移、内存策略。除此之外还与UI展现良好结合。

Core data 并不是一款关系数据库,它拥有但不限于关系数据库的功能,它还具有模型设计器、数据访问层的功能。

使用Core data 作为持久化解决方案时,自上而下可分为如下几层(称为:Core data Stack):

                                Managed Object Context
                                                  |
                                Persistent Store Coordinator ---- Managed Obhect Model
                                                  |
                                Pesistent Object Store


安装:


安装的方式只有一步,引入CoreData.framework 即可.

使用:


使用Core Data起步最先要了解和熟悉的类是以下三个:

1:NSManagedObjectModel(上下文)
构建整个数据库的表结构,表字段类型,表与表之间的关系(Relationship)等等凡是和数据结构有关的定义都通过此类来管理.
通过NSManagedObjecModel初始化,一旦初始化成功,就有了SQLite的DB,就已经有了完善的表结构关系。

2:NSPersistentStoreCoordinator(持久性数据协调器)
NSPersistentStoreCoordinator是真正意义上和SQLite打交道的类,主要根据NSManagedObjectModel 执行表结构的建立,通过NSManagedObjectContext 的命令执行数据交互。

3:NSManagedObjectContext(管理上下文)
NSManagedObjectContext是我们在开发中主要交互的类,数据的增删改查都通过上下文去触发命令并返回结果. 根据一个NSPersistentStoreCoordinator 完成初始化.



正文:

下面开始代码部分,首先是创建工程,记得创建工程的时候勾选上 CoreData 选项.



勾选了这个选项后,我们的程序中会自动出现 CoreDataTest.xcdatamodeld 这个文件.这个文件的使用方式我们会在下文中介绍.
同时 在 AppDelegate 也会自动出现代码.

AppDelegate.h
#import <UIKit/UIKit.h>#import <CoreData/CoreData.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

AppDelegate.m   .m 中主要介绍下面四个方法
#pragma mark - Core Data stack//返回路径- (NSURL *)applicationDocumentsDirectory {}//描述应用程序的数据模型,这个模型包含实体(Entity),特性(Property),读取请求(Fetch Request)等。- (NSManagedObjectModel *)managedObjectModel {}//相当于数据文件管理器,处理底层的对数据文件的读取与写入。一般我们无需与它打交道。- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {}//参与对数据对象进行各种操作的全过程,并监测数据对象的变化,以提供对 undo/redo 的支持及更新绑定到数据的 UI。- (NSManagedObjectContext *)managedObjectContext {}


之后创建一个根视图,因为我们需要下面所有页面都要用到 UIBarButtonItem.

CommonViewController.h

#import <UIKit/UIKit.h>@interface CommonViewController : UIViewController-(void)addData2DB:(NSString *)name;-(void)deleteFromDB:(NSString *)name;-(void)updateFromDB:(NSString *)name;@end

CommonViewController.m

#import "CommonViewController.h"@interface CommonViewController ()@property (nonatomic, strong) UIAlertController *alertController;@end@implementation CommonViewController- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view.    UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:(UIBarButtonSystemItemAdd) target:self action:@selector(addAction:)];    [self.navigationItem setRightBarButtonItem:item];            if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) {        //设置边缘是否        [self setEdgesForExtendedLayout:(UIRectEdgeNone)];    }        }-(void)addAction:(id)sender{    //使用懒加载     延迟加载,当程序走到该处,才开始执行 get 方法 , 这时候才会去创建alertController    [self presentViewController:self.alertController animated:YES completion:nil];}//实现触发方法-(UIAlertController *)alertController{    if(!_alertController){        _alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"您想要保存么?" preferredStyle:(UIAlertControllerStyleAlert)];        UIAlertAction *saveAction = [UIAlertAction actionWithTitle:@"保存" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {            NSLog(@"保存成功");            [self save];        }];        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"删除" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {            NSLog(@"删除成功");            [self cancel];        }];        UIAlertAction *updateAction = [UIAlertAction actionWithTitle:@"更新" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {            NSLog(@"更新成功");            [self  update];        }];        [_alertController addAction:saveAction];        [_alertController addAction:cancelAction];        [_alertController addAction:updateAction];                [_alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {            textField.placeholder = @"请输入...";        }];    }    return _alertController;}#pragma mark - 预留接口//实现方法      2 --> To    4 --> for-(void)addData2DB:(NSString *)name{    }-(void)deleteFromDB:(NSString *)name{    }-(void)updateFromDB:(NSString *)name{}#pragma mark - 接口方法-(void)save{    //打印触发的方法名    NSLog(@"%@",NSStringFromSelector(_cmd));        //获取输入框中内容并放入本地    UITextField *field = [self.alertController.textFields firstObject];    NSLog(@"%@",field.text);    self.alertController = nil;        //将数据存入数据库中    [self addData2DB:field.text];}-(void)cancel{    //打印触发的方法名    NSLog(@"%@",NSStringFromSelector(_cmd));    //获取输入框中内容并放入本地    UITextField *field = [self.alertController.textFields firstObject];    //删除    [self deleteFromDB:field.text];        self.alertController = nil;}-(void)update{    //打印触发的方法名    NSLog(@"%@",NSStringFromSelector(_cmd));    //获取输入框中内容并放入本地    UITextField *field = [self.alertController.textFields firstObject];    //删除    [self updateFromDB:field.text];        self.alertController = nil;}@end

这时候创建我们的根视图 ProvinceViewController 继承与 上面的 CommonViewController


之后记得在 didFinishLaunching 里设置根视图, 创建 Navigation.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    // Override point for customization after application launch.        self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];    [self.window makeKeyAndVisible];        self.window.rootViewController = [[UINavigationController alloc]initWithRootViewController:[ProvinceViewController new]];        return YES;}

这时候我们就可以去 CoreDataTest.xcdatamodeld 中添加我们的数据了



这时候我们可以直接创建对应的 modal 类   

注意 : 一定不要再生成文件中去修改东西,
如果需要修改,删除当前,重新回到CoreDataTest.xcdatamodeld中书写完成再生成

这个时候其实就可以回去添加 CoreData 了 , 但是我们再去生成一个类.
DBManager.h
#import <Foundation/Foundation.h>@class Province;@interface DBManager : NSObject+(id)shareManager;//声明添加数据库的方法-(void)addProvince:(NSString *)name;-(void)deleteProvince:(NSString *)name;-(void)upDataProvince:(NSString *)name;-(NSArray *)selectAllProvince;@end

DBManager.m
#import "DBManager.h"#import "AppDelegate.h"#import "Province.h"#import "Global.h"#import "City.h"@implementation DBManager//创建一个单例+(id)shareManager{    static DBManager *instance = nil;    static dispatch_once_t onceToken;    dispatch_once(&onceToken,^{        instance = [[[self class]alloc] init];    });    return instance;    }//保存更新-(void)save{    NSError *error = nil;    if (![[appdelegate managedObjectContext]save:&error]) {        NSLog(@"保存数据成功 --> %@",[error userInfo]);    }    else{        NSLog(@"保存数据成功");        [[NSNotificationCenter defaultCenter] postNotificationName:operateDBSuccessNotification object:nil];    }}//添加-(void)addProvince:(NSString *)name{//    //获取代理对象//    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];    NSArray *array = [self selectProvinceWithName:name];    if (array.count > 0) {        NSLog(@"添加数据已经存在");        return;    }    //创建实体描述对象 对应表名    NSEntityDescription *des = [NSEntityDescription entityForName:@"Province" inManagedObjectContext:[appdelegate managedObjectContext]];    //生成哪个表的管理对象    Province *province = [[Province alloc]initWithEntity:des insertIntoManagedObjectContext:[appdelegate managedObjectContext]];    province.name = name;        [self save];}//查询-(NSArray *)selectProvinceWithName:(NSString *)name{    //对这个表发起查询请求    NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:@"Province"];    //查询条件    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@",name];    //    [request setPredicate:predicate];    //    NSError *error = nil;    //    NSArray *array = [[appdelegate managedObjectContext] executeFetchRequest:request error:&error];    if (error) {        NSLog(@"查询失败 -> %@",[error userInfo]);    }    return array;}//选择-(NSArray *)selectAllProvince{    //对这个表发起查询请求    NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:@"Province"];    //    NSError *error = nil;    //    NSArray *array = [[appdelegate managedObjectContext] executeFetchRequest:request error:&error];    if (error) {        NSLog(@"查询失败 -> %@",[error userInfo]);    }    return array;}//删除-(void)deleteProvince:(NSString *)name{    //查询方法    NSArray *array = [self selectProvinceWithName:name];    if ([array count] > 0) {        //想获取数据,首先遍历数组        [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {            //获取到的数据,并进行强转            Province *province = (Province *)obj;                        //删除省份对应下的城市            [self deleteCitiesInProvince:province];                        //获取上下文并删除            [[appdelegate managedObjectContext]deleteObject:province];        }];        //保存        [self save];    }    else{        NSLog(@"该条数据不存在");    }}//-(void)deleteCitiesInProvince:(Province *)province{#pragma mark - 通过对象来查找//    //查询请求//    NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:@"City"];//    //查询条件//    NSPredicate *pre = [NSPredicate predicateWithFormat:@"province = %@",province];//    ////    [request setPredicate:pre];//    ////    NSError *error = nil;//    //上下文执行,返回结果//    NSArray *array = [[appdelegate managedObjectContext] executeFetchRequest:request error:&error];//    //遍历结果,依次删除//    if (array.count > 0) {//        [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {//            City *city = (City *)obj;//            [self deleteCity:city.name];//        }];//    }#pragma mark - 通过关联对象查找    NSSet *set = province.city;    [set enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {        City *city = (City *)obj;        [self deleteCity:city.name];    }];    }//更新-(void)upDataProvince:(NSString *)name{    //1.查询数据库    NSArray *array = [self selectAllProvince];    if (array.count > 0) {    //2.遍历查询的数据        [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {            //3.获取每个 Province 对象 , 取得 Name            Province *province = (Province *)obj;            NSRange range = [province.name rangeOfString:@"省"];            //4.修改 name ,得到新的 name 并将其 重新赋值给 province 的 name 属性            if(range.location != NSNotFound){                NSString *newName = [province.name stringByReplacingCharactersInRange:range withString:name];                province.name = newName;            }        }];        //5.储存        [self save];    }}

生成
Global.h
#ifndef CoreDataTest_Global_h#define CoreDataTest_Global_h//appDelegate#define appdelegate (AppDelegate *)[[UIApplication sharedApplication]delegate]//operate db notification#define operateDBSuccessNotification @"operateDBSuccessNotification"#endif

重新回到ProvinceViewController

ProvinceViewController.h
#import <UIKit/UIKit.h>#import "CommonViewController.h"@interface ProvinceViewController : CommonViewController@property (nonatomic, strong) UITableView *tableView;@end

ProvinceViewController.m
#import "ProvinceViewController.h"#import "DBManager.h"#import "Global.h"#import "CityViewController.h"@interface ProvinceViewController () <UITableViewDataSource,UITableViewDelegate>//存放数据@property (nonatomic, strong) NSArray *provinceArray;@end@implementation ProvinceViewController- (void)viewDidLoad {    [super viewDidLoad];        self.view.backgroundColor = [UIColor colorWithRed:0.649 green:1.000 blue:0.471 alpha:1.000];        self.title = @"省  份";        //注册通知    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(upTableData) name:operateDBSuccessNotification object:nil];    [self upTableData];}-(void)upTableData {    //使用 GCD    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        //放入子线程中去进行请求        self.provinceArray = [[DBManager shareManager] selectAllProvince];        dispatch_async(dispatch_get_main_queue(), ^{//            if ([self.provinceArray count] > 0) {                [self.tableView reloadData];//            }        });    });}-(UITableView *)tableView{    if (!_tableView) {        _tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:(UITableViewStylePlain)];        _tableView.delegate = self;        _tableView.dataSource = self;        [self.view addSubview:_tableView];    }    return _tableView;}#pragma mark - overriade super method (重写父类方法) //实现方法      2 --> To    4 --> for-(void)addData2DB:(NSString *)name{        [super addData2DB:name];    //调用方法    [[DBManager shareManager]addProvince:name];}-(void)deleteFromDB:(NSString *)name{    [[DBManager shareManager]deleteProvince:name];}-(void)updateFromDB:(NSString *)name{    [[DBManager shareManager]upDataProvince:name];}-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    return [self.provinceArray count];}-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    static NSString *identifity = @"cell";    UITableViewCell *cell = [tableView dequeueReusableHeaderFooterViewWithIdentifier:identifity];    if (!cell) {        cell = [[UITableViewCell alloc]initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifity];    }    cell.textLabel.text = [self.provinceArray[indexPath.row] name];    return cell;}#pragma mark - 实现页面跳转-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{    CityViewController *cityVC = [[CityViewController alloc]init];        //将当前的省份传入下一个页面    cityVC.currentProvince = self.provinceArray[indexPath.row];        [self.navigationController pushViewController:cityVC animated:YES];}@end

这时候基本就完成我们的 CoreData 基本功能了.来看看效果.


实现效果:





附录:

在日常使用中经常会出现CoreData 版本问题.解决方法如下:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator{    if (_persistentStoreCoordinator != nil) {        return _persistentStoreCoordinator;    }    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"_6Kr.sqlite"];    NSError *error = nil;    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:@{NSMigratePersistentStoresAutomaticallyOption:@YES, NSInferMappingModelAutomaticallyOption:@YES} 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;}





1 0