D32:CoreData

作者: Vinc | 来源:发表于2015-06-16 20:48 被阅读615次

    目录

    一. CoreData简介

    1. CoreData是apple自己封装的数据库操作的一个框架, 是一种面向对象的方式来操作数据的框架, 底层是基于SQLite的
    2. 需要用到的几个基本类型

    二. CoreData使用

    1. 图形化操作
    2. 常规的创建表格, 创建Cell
    3. 核心内容:创建上下文对象
    4. DBManager类中 "增删改查" 的方法
    5. 详情视图控制器(相册部分内容需要回顾加深)
    6. ViewController.m(在查询时由于数据量可能较大, 因此使用GCD知识; 删除时方法- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath要注意删除顺序)

    三. 当CoreData数据库升级时我们需要

    1. 假设我们添加一个属性, 首先我们要建立一个新版本的模型, NSManagedObject Subclass也要重新生成
    2. 修改DBManager.m中的- (void)createManagedContext方法
    3. 模拟处理country属性
    4. 如果原有的数据库文件内有数据的话, 需要删除原来的数据库文件, 否则可能会报错(可能是数据结构问题)

    一. CoreData简介

    1. CoreData是apple自己封装的数据库操作的一个框架, 是一种面向对象的方式来操作数据的框架, 底层是基于SQLite的
    2. 需要用到的几个基本类型
    1. NSManagedObjectModel: 对应于所有实体类的描述文件, 文件里面包含很多类的属性和关系的描述
    • NSManagedObject: 对应于一个实体类, 所有的CoreData操作的实体类都是它的子类
    • NSPersistentStoreCoordinator: 用来关联类和对应的数据库文件
    • NSmanagedContext: 上下文对象, 用来操作数据库的增删改查

    二. CoreData使用

    1. 图形化操作
    新建一个Core Data分类中的Data Model文件, 格式为"xcdatamodeld" 点击左下角的Add Entity增加Entity 修改Entity名为"Computer", 为"Computer"增加2个属性("Computer"只是测试用) 选中"Computer"这个Entity, 点击"Editor -> Create NSManagedObject Subclass" 因为只有一个DataModel, 默认勾选状态, 直接Next即可 当前只有一个Entity, 同样是默认勾选状态, 直接Next即可 在Group的选项中要选择当前项目, 选中Target 自动创建了Computer类 创建"User"这个Entity, 为其添加4个属性 换一种方式用"Command+N"来新建NSManagedObject Subclass 当前有2个Entity, 我们选择需要的"User" 创建完成
    2. 常规的创建表格, 创建Cell
    3. 核心内容:创建上下文对象
    //
    //  DBManager.m
    //  01_CoreDataDemo
    
    #import "DBManager.h"
    #import <CoreData/CoreData.h>
    
    @implementation DBManager
    {
    #warning 用来处理数据库增删改查的对象
        NSManagedObjectContext *_context;
    }
    
    + (instancetype)sharedManager
    {
        static DBManager *manager = nil;
        
        static dispatch_once_t onceToken;
        
        dispatch_once(&onceToken, ^{
            manager = [[DBManager alloc] init];
        });
        
        return manager;
    }
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            [self createManagedContext];
        }
        return self;
    }
    
    // 创建上下文对象
    - (void)createManagedContext
    {
        // 1. 创建描述文件的关联对象
        // 文件的路径
    #warning 扩展名不是写"xcdatamodeld"
        NSString *path = [[NSBundle mainBundle] pathForResource:@"MyUser" ofType:@"momd"];
        
        NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:[NSURL URLWithString:path]];
        
        // 2. 关联数据库文件(SQLite)
        // 先关联数据模型描述文件对应的对象
        NSPersistentStoreCoordinator *coor = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
        // 再关联数据库
        /*
         第一个参数: 类型(建议用SQLite类型)
         第二个参数: 传nil即可
         第三个参数: 文件的路径
         第四个参数: 选项信息
         第五个参数: 错误信息
         */
        NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/user.sqlite"];
        NSURL *sqliteUrl = [NSURL URLWithString:filePath];
        
        [coor addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqliteUrl options:nil error:nil];
        
        // 3. 创建上下文对象, 跟上面的对象关联起来
        _context = [[NSManagedObjectContext alloc] init];
        _context.persistentStoreCoordinator = coor;
    }
    
    @end
    
    4. DBManager类中 "增删改查" 的方法
    /*
     在添加数据之前, 判断相同userId的数据是否已经存在, 如果是, 先删除以前存在的数据, 再重新添加
     */
    - (void)verifyOldUserData:(NSDictionary *)userDict
    {
        // 先判断是否存在
        // 查询
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:_context];
        
        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        request.entity = entity;
        
        // 设置查询条件
        if ([[userDict allKeys] containsObject:kUserId]) {
            
            // 谓词
            NSPredicate *p = [NSPredicate predicateWithFormat:@"userId == %@", userDict[kUserId]];
            request.predicate = p;
            
            // 执行查询操作
            NSError *error;
            NSArray *array = [_context executeFetchRequest:request error:&error];
            if (error) {
                NSLog(@"%@", error);
            }
            
            if (array.count > 0) {
                // 删除以前的数据
                for (User *tmpUser in array) {
                    [_context deleteObject:tmpUser];
                }
                // 保存(可以和上面的操作用同一个error)
                [_context save:&error];
                if (error) {
                    NSLog(@"===%@", error);
                }
            }
        }
    }
    
    - (void)insertUser:(NSDictionary *)userDict
    {
        [self verifyOldUserData:userDict];
        
        // NSEntityDescription
        // 第一个参数: 类名
        // 第二个参数: 上下文
        User *user = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:_context];
        if ([[userDict allKeys] containsObject:kUserId]) {
            user.userId = userDict[kUserId];
        }
        if ([[userDict allKeys] containsObject:kUserName]) {
            user.userName = userDict[kUserName];
        }
        if ([[userDict allKeys] containsObject:kHeadImage]) {
            user.headImage = userDict[kHeadImage];
        }
        if ([[userDict allKeys] containsObject:kAge]) {
            user.age = userDict[kAge];
        }
        if ([[userDict allKeys] containsObject:kCountry]) {
            user.country = userDict[kCountry];
        }
    
        // 存储
        NSError *error;
        [_context save:&error];
        if (error) {
            NSLog(@"%@", error.localizedDescription);
        }
    }
    
    // 查询所有数据
    - (NSArray *)searchAllUsers
    {
        // 创建一个NSEntityDescription类型的对象
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:_context];
        
        // 查询对象(包括查询的数据对象所属的类型等信息)
        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        
        request.entity = entity;
        
        // 执行查询操作
        NSError *error;
        NSArray *array = [_context executeFetchRequest:request error:&error];
        if (error) {
            NSLog(@"%@", error);
        }
        
        return array;
    }
    
    // 删除
    - (void)deleteUser:(User *)user
    {
        [_context deleteObject:user];
        
        // 保存
        NSError *error;
        BOOL flag = [_context save:&error];
        if (error) {
            NSLog(@"%@", error);
        }
        if (flag == NO) {
            NSLog(@"删除失败");
        }
    }
    
    // 修改数据
    - (void)updateUser:(NSDictionary *)userDict
    {
        // 1. 查询修改的对象
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:_context];
        // 2. 查询的对象
        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        request.entity = entity;
        
        // 3. 查询条件
        NSPredicate *p = [NSPredicate predicateWithFormat:@"userId = %@", userDict[kUserId]];
        request.predicate = p;
        
        NSError *error;
        NSArray *array = [_context executeFetchRequest:request error:&error];
        if (error) {
            NSLog(@"%@", error);
        } else {
            // 修改数据
            for (User *tmpUser in array) {
                // 修改属性值
                if ([[userDict allKeys] containsObject:kUserName]) {
                    tmpUser.userName = userDict[kUserName];
                }
                if ([[userDict allKeys] containsObject:kAge]) {
                    tmpUser.age = userDict[kAge];
                }
                if ([[userDict allKeys] containsObject:kHeadImage]) {
                    tmpUser.headImage = userDict[kHeadImage];
                }
            }
            
            // 存储
            [_context save:&error];
            if (error) {
                NSLog(@"%@", error);
            }
        }
    }
    
    @end
    
    5. 详情视图控制器(相册部分内容需要回顾加深)
    //
    //  DetailViewController.h
    //  01_CoreDataDemo
    
    #import <UIKit/UIKit.h>
    #import "User.h"
    
    @interface DetailViewController : UIViewController
    
    @property (nonatomic, strong) User *user;
    
    @end
    
    #import "DetailViewController.h"
    #import "MyUtility.h"
    #import "DBManager.h"
    
    #define kUserId         (@"userId")
    #define kUserName       (@"userName")
    #define kAge            (@"age")
    #define kHeadImage      (@"headImage")
    #define kCountry        (@"country")
    
    @interface DetailViewController () <UINavigationControllerDelegate ,UIImagePickerControllerDelegate>
    {
        // 用户Id
        UITextField *_userIdTextField;
        // 用户名
        UITextField *_userNameTextField;
        // 年龄
        UITextField *_ageTextField;
        // 图片
        UIButton *_imageBtn;
    }
    
    @end
    
    @implementation DetailViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        self.view.backgroundColor = [UIColor whiteColor];
    
        // 用户名
        UILabel *userIdLabel = [MyUtility createLabelWithFrame:CGRectMake(30, 100, 80, 30) title:@"用户ID:" font:nil];
        _userIdTextField = [MyUtility createTextField:CGRectMake(130, 100, 200, 30) placeHolder:@"请输入用户ID"];
        [self.view addSubview:userIdLabel];
        [self.view addSubview:_userIdTextField];
        
        // 用户名
        UILabel *userNameLabel = [MyUtility createLabelWithFrame:CGRectMake(30, 150, 80, 30) title:@"用户名:" font:nil];
        _userNameTextField = [MyUtility createTextField:CGRectMake(130, 150, 200, 30) placeHolder:@"请输入用户名"];
        [self.view addSubview:userNameLabel];
        [self.view addSubview:_userNameTextField];
        
        // 年龄
        UILabel *userAgeLabel = [MyUtility createLabelWithFrame:CGRectMake(30, 200, 80, 30) title:@"年龄:" font:nil];
        _ageTextField = [MyUtility createTextField:CGRectMake(130, 200, 200, 30) placeHolder:@"请输入年龄"];
        [self.view addSubview:userAgeLabel];
        [self.view addSubview:_ageTextField];
    
        // 头像
        UILabel *imageLabel = [MyUtility createLabelWithFrame:CGRectMake(30, 280, 80, 30) title:@"头像" font:nil];
        _imageBtn = [MyUtility createButtonWithFrame:CGRectMake(130, 260, 100, 100) title:nil backgroundImageName:nil target:self action:@selector(clickBtn)];
        _imageBtn.layer.cornerRadius = 8;
        _imageBtn.clipsToBounds = YES;
    #warning 边框
        _imageBtn.layer.borderColor = [UIColor cyanColor].CGColor;
        _imageBtn.layer.borderWidth = 2;
        [self.view addSubview:imageLabel];
        [self.view addSubview:_imageBtn];
        
        // 保存按钮
        UIBarButtonItem *rightSaveItem = [[UIBarButtonItem alloc] initWithTitle:@"保存" style:UIBarButtonItemStylePlain target:self action:@selector(saveAction)];
        self.navigationItem.rightBarButtonItem = rightSaveItem;
        
        // 如果是修改界面, 显示传过来的数据
        if (self.user) {
            _userIdTextField.text = self.user.userId.stringValue;
            _userNameTextField.text = self.user.userName;
            _ageTextField.text = self.user.age.stringValue;
            // headImage
            [_imageBtn setBackgroundImage:[UIImage imageWithData:self.user.headImage] forState:UIControlStateNormal];
        }
        
        
    }
    
    - (void)saveAction
    {
        NSMutableDictionary *userDict = [NSMutableDictionary dictionary];
        
        if (_userIdTextField.text.length > 0) {
            NSNumber *userId = [NSNumber numberWithInt:_userIdTextField.text.intValue];
            [userDict setObject:userId forKey:kUserId];
        }
        if (_userNameTextField.text.length > 0) {
            [userDict setObject:_userNameTextField.text forKey:kUserName];
        }
        if (_ageTextField.text.length > 0) {
            NSNumber *userAge = [NSNumber numberWithInt:_ageTextField.text.intValue];
            [userDict setObject:userAge forKey:kAge];
        }
        if ([_imageBtn backgroundImageForState:UIControlStateNormal]) {
            // 存储图片
            UIImage *bgImage = [_imageBtn backgroundImageForState:UIControlStateNormal];
            NSData *data = UIImagePNGRepresentation(bgImage);
            [userDict setObject:data forKey:kHeadImage];
        }
    
        [userDict setObject:@80 forKey:kCountry];
        
        if (self.user) {
            // 修改
            [[DBManager sharedManager] updateUser:userDict];
        } else {
            // 存储
            [[DBManager sharedManager] insertUser:userDict];
        }
    }
    
    - (void)clickBtn
    {
    #warning 从相册选图片
        UIImagePickerController *ctrl = [[UIImagePickerController alloc] init];
        ctrl.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        ctrl.delegate = self;
        
        [self presentViewController:ctrl animated:YES completion:nil];
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    #pragma mark - UINavigationControllerDelegate ,UIImagePickerControllerDelegate代理
    - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
    {
        // 获取图片
        UIImage *image = info[UIImagePickerControllerOriginalImage];
        
        [_imageBtn setBackgroundImage:image forState:UIControlStateNormal];
        
        [picker dismissViewControllerAnimated:YES completion:nil];
    }
    
    - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
    {
        [picker dismissViewControllerAnimated:YES completion:nil];
    }
    
    @end
    
    6. ViewController.m(在查询时由于数据量可能较大, 因此使用GCD知识; 删除时方法- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath要注意删除顺序)
    //
    //  ViewController.m
    //  01_CoreDataDemo
    
    #import "ViewController.h"
    #import "UserCell.h"
    #import "DetailViewController.h"
    #import "DBManager.h"
    
    @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
    
    // 数据源
    @property (nonatomic, strong) NSMutableArray *dataArray;
    // 表格
    @property (nonatomic, strong) UITableView *tableView;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    
        // 创建表格
        [self createTableView];
        
        // 添加按钮
        UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(gotoAddPage)];
        self.navigationItem.rightBarButtonItem = rightItem;
    }
    
    - (void)gotoAddPage
    {
        // 跳转界面
        DetailViewController *dCtrl = [[DetailViewController alloc] init];
        [self.navigationController pushViewController:dCtrl animated:YES];
    }
    
    - (NSMutableArray *)dataArray
    {
        if (!_dataArray) {
            _dataArray = [NSMutableArray array];
        }
        return _dataArray;
    }
    
    - (void)createTableView
    {
        self.automaticallyAdjustsScrollViewInsets = NO;
        
        _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, self.view.bounds.size.width, self.view.bounds.size.height - 64) style:UITableViewStylePlain];
        _tableView.delegate = self;
        _tableView.dataSource = self;
        [self.view addSubview:_tableView];
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    - (void)viewWillAppear:(BOOL)animated
    {
        // 查询所有数据
        // 查询数据量可能较大, 因此开一个线程来进行查询
        __weak ViewController *weakSelf = self;
        dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(globalQueue, ^{
            
            NSArray *array = [[DBManager sharedManager] searchAllUsers];
            weakSelf.dataArray = [NSMutableArray arrayWithArray:array];
            
            // 回到主线程刷新UI
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf.tableView reloadData];
            });
            
        });
    }
    
    #pragma mark - UITableView代理
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.dataArray.count;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *cellId = @"cellId";
        UserCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
        if (cell == nil) {
            cell = [[[NSBundle mainBundle] loadNibNamed:@"UserCell" owner:nil options:nil] lastObject];
            NSLog(@"%ld", indexPath.row);
        }
    
        // 显示数据
        User *model = self.dataArray[indexPath.row];
        NSLog(@"%@", model.country);
        [cell config:model];
    
        return cell;
    }
    
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return 80;
    }
    
    // 删除
    - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (editingStyle == UITableViewCellEditingStyleDelete) {
    #warning 删除数据的顺序(从数据库删除和数据源删除的顺序不能调换)
            // 从数据库删除
            User *user = self.dataArray[indexPath.row];
            [[DBManager sharedManager] deleteUser:user];
            
            // 数据源删除
            [self.dataArray removeObjectAtIndex:indexPath.row];
            
            // UI删除
            [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        }
    }
    
    // 修改
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        DetailViewController *dCtrl = [[DetailViewController alloc] init];
        dCtrl.user = self.dataArray[indexPath.row];
        
        [self.navigationController pushViewController:dCtrl animated:YES];
    }
    
    @end
    

    三. 当CoreData数据库升级时我们需要

    首先要明白的是升级的含义:

    升级的意思是数据库里面的类增加了或者类的属性添加或修改了
    升级是当前需要发布的版本跟AppStore线上版本比较而言的

    1. 假设我们添加一个属性, 首先我们要建立一个新版本的模型, NSManagedObject Subclass也要重新生成
    选中, 点击Editor -> Add Model Version 更改当前的Model Version
    2. 修改DBManager.m中的- (void)createManagedContext方法
    // 创建上下文对象
    - (void)createManagedContext
    {
        // 1. 创建描述文件的关联对象
        // 文件的路径
        …………………………………………………………………………………………  
        
        // 2. 关联数据库文件(SQLite)
        …………………………………………………………………………………………        
        
        /*
         - (NSPersistentStore *)addPersistentStoreWithType:(NSString *)storeType configuration:(NSString *)configuration URL:(NSURL *)storeURL options:(NSDictionary *)options error:(NSError **)error;
         
         方法中的options在CoreData数据库升级的时候使用
         升级的意思是数据库里面的类增加了或者类的属性添加或修改了
         升级是当前需要发布的版本跟AppStore线上版本比较而言的
         */
        NSDictionary *dict = @{NSMigratePersistentStoresAutomaticallyOption:[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption:[NSNumber numberWithBool:YES]};
        [coor addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqliteUrl options:dict error:nil];
        
        // 3. 创建上下文对象, 跟上面的对象关联起来
        …………………………………………………………………………………………  
    }
    
    3. 模拟处理country属性
    4. 如果原有的数据库文件内有数据的话, 需要删除原来的数据库文件, 否则可能会报错(可能是数据结构问题)

    相关文章

      网友评论

        本文标题:D32:CoreData

        本文链接:https://www.haomeiwen.com/subject/ophsqttx.html