iOS技术专题IOS文章收集iOS

iOS CoreData介绍和使用(以及一些注意事项)

2016-07-15  本文已影响14598人  Pocket

iOS CoreData介绍和使用(以及一些注意事项)

最近花了一点时间整理了一下CoreData,对于经常使用SQLite的我来说,用这个真的有点用不惯,个人觉得实在是没发现什么亮点,不喜勿喷啊。不过这门技术的出现也有其存在价值,这是不可否认的事实,即使是不喜欢我们也得去了解一下,因为你不用别人会用,这年头都多人开发,多学点还是有好处的。废话不多说了,该开始正经事了。

CoreData介绍

CoreData是一个模型层的技术,也是一种持久化技术(数据库),它能将模型对象的状态持久化到磁盘里,我们不需要使用SQL语句就能对它进行操作。不过在性能方面是弱于直接使用SQLite的。

CoreData使用

理论我就不多说了,觉得我说的不够详细的可以自行搜索关于更多的CoreData介绍。

屏幕快照 2016-07-07 下午10.40.16.png

名字虽然可以任意取,但最好还是取和自己存储数据库名字一样的名字。这样可读性更高些。(ps:这个文件就相当于数据库文件一样,数据库文件中可以有多个表,表中可以有各个字段值,这里也可以有多个实体,每个实体有各个键值)

屏幕快照 2016-07-07 下午10.40.40.png

通过上面的操作就可以创建一个.xcdatamodeld文件,现在我们点击这个文件,在最下面找到Add Entity按钮,进行实体添加。

屏幕快照 2016-07-13 下午8.50.10.png

现在我们添加了一个名字为Entity的实体,这个名字是默认名字,现在我们可以对他进行名字的更改,对它双击一下即可修改名字,或者在xcode右边的信息栏中也可以修改

屏幕快照 2016-07-13 下午9.09.38.png

通过上面操作我们已经创建好了实体的名字,现在我们需要往实体中添加我们需要的键值。(ps:相当于数据库表中的字段),具体操作看图说话(实体之间的关联我就不介绍了,有兴趣的可以自行搜索资料,个人觉得会了单个实体创建外加查询就行,关联也无非是找到关联的实体中共同的键值从而取得另外一个实体对象,我们可以直接先从一个实体取得指定键值对应的属性,再通过属性值去查询另一个实体,只是没关联的那么方便而已)。

屏幕快照 2016-07-13 下午9.17.43.png 屏幕快照 2016-07-13 下午9.35.57.png

选中自己需要关联的.xcdatamodeld文件名称,点击下一步即可。

屏幕快照 2016-07-13 下午9.47.24.png

选中.xcdatamodeld文件中需要关联的实体对象,点击下一步然后在选择存储目录即可。

屏幕快照 2016-07-13 下午9.48.06.png

完成后会发现自动生成了实体名称对应的类和扩展类(Entity.h/.m和Entity+CoreDataProperties.h/.m)

屏幕快照 2016-07-13 下午10.23.22.png

到这里就完成了整一个创建的流程,个人觉得说的有点过于详细了(导致看起来有点啰嗦)

我在试验的过程中发现,如果我把关联的类从项目中删除,在对实体名称再次修改后,重新创建关联类,发现类名还是原来的实体名称显示,可能xcode没刷新,反正最后我是删除实体重新Add Entity之后才有用,所以为了不必要麻烦,最好还是想好实体名称后再创建关联类。

我就以我自己创建的类来说明

#import "UploadEntity.h"

NS_ASSUME_NONNULL_BEGIN

@interface UploadEntity (CoreDataProperties)

@property (nullable, nonatomic, retain) NSString *fileName;
@property (nullable, nonatomic, retain) NSString *fileSize;
@property (nullable, nonatomic, retain) NSNumber *fileType;
@property (nullable, nonatomic, retain) NSNumber *finishStatus;
@property (nullable, nonatomic, retain) NSData *imageData;
@property (nullable, nonatomic, retain) NSNumber *time;
@property (nullable, nonatomic, retain) NSString *urlPath;

@end

NS_ASSUME_NONNULL_END

@end

```
```objc
//
//  CoreDataAPI.m
//  TedcallStorage
//
//  Created by  tedcall on 16/7/1.
//  Copyright © 2016年 pocket. All rights reserved.
//

/**
 *  知识点:
 NSManagedObject:
     通过Core Data从数据库中取出的对象,默认情况下都是NSManagedObject对象.
     NSManagedObject的工作模式有点类似于NSDictionary对象,通过键-值对来存取所有的实体属性.
     setValue:forkey:存储属性值(属性名为key);
     valueForKey:获取属性值(属性名为key).
     每个NSManagedObject都知道自己属于哪个NSManagedObjectContext

 NSManagedObjectContext:
    负责数据和应用库之间的交互(CRUD,即增删改查、保存等接口都在这个对象中).
    所有的NSManagedObject都存在于NSManagedObjectContext中,所以对象和context是相关联的
    每个 context 和其他 context 都是完全独立的
    每个NSManagedObjectContext都知道自己管理着哪些NSManagedObject

 NSPersistentStoreCoordinator:
    添加持久化存储库,CoreData的存储类型(比如SQLite数据库就是其中一种)
    中间审查者,用来将对象图管理部分和持久化部分捆绑在一起,负责相互之间的交流(中介一样)

 NSManagedObjectModel:
    Core Data的模型文件

 NSEntityDescription:
    用来描述实体:相当于数据库表中一组数据描述
 */

#import "CoreDataAPI.h"
#import <CoreData/CoreData.h>
@interface CoreDataAPI()
/**
 *  数据模型对象
 */
@property (nonatomic,strong) NSManagedObjectModel *model;
/**
 *  上下文
 */
@property (nonatomic,strong) NSManagedObjectContext *context;
/**
 *  持久性存储区
 */
@property (nonatomic,strong) NSPersistentStoreCoordinator *persistent;
@end

@implementation CoreDataAPI

- (instancetype)initWithCoreData:(NSString *)entityName modelName:(NSString *)modelName sqlPath:(NSString *)sqlPath success:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
    if (self = [super init]) {
        // 断言(实体名称和存储路径是否为nil)
        // ...

        _entityName = entityName;
        _modelName = modelName;
        _sqlPath = sqlPath;
        // 初始化上下文
        self.context = [[NSManagedObjectContext alloc] init];
        if (modelName) {
            //获取模型路径
            NSURL *modelURL = [[NSBundle mainBundle] URLForResource:modelName withExtension:@"momd"];
            //根据模型文件创建模型对象
            self.model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
        } else { // 从应用程序包中加载模型文件
            self.model = [NSManagedObjectModel mergedModelFromBundles:nil];
        }

        // 以传入模型方式初始化持久化存储库
        self.persistent = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.model];
        /*
         持久化存储库的类型:
         NSSQLiteStoreType  SQLite数据库
         NSBinaryStoreType  二进制平面文件
         NSInMemoryStoreType 内存库,无法永久保存数据
         虽然这3种类型的性能从速度上来说都差不多,但从数据模型中保留下来的信息却不一样
         在几乎所有的情景中,都应该采用默认设置,使用SQLite作为持久化存储库
         */
        // 添加一个持久化存储库并设置类型和路径,NSSQLiteStoreType:SQLite作为存储库
        NSError *error = nil;
        [self.persistent addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlPath] options:nil error:&error];
        if (error) {
            NSLog(@"添加数据库失败:%@",error);
            if (fail) {
                fail(error);
            }
        } else {
             NSLog(@"添加数据库成功");
            // 设置上下文所要关联的持久化存储库
            self.context.persistentStoreCoordinator = self.persistent;
            if (success) {
                success();
            }
        }
    }

    return self;
}

// 添加数据
- (void)insertNewEntity:(NSDictionary *)dict success:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
    if (!dict||dict.allKeys.count == 0) return;
    // 通过传入上下文和实体名称,创建一个名称对应的实体对象(相当于数据库一组数据,其中含有多个字段)
    NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName:self.entityName inManagedObjectContext:self.context];
    // 实体对象存储属性值(相当于数据库中将一个值存入对应字段)
    for (NSString *key in [dict allKeys]) {
        [newEntity setValue:[dict objectForKey:key] forKey:key];
    }
    // 保存信息,同步数据
    NSError *error = nil;
    BOOL result = [self.context save:&error];
    if (!result) {
        NSLog(@"添加数据失败:%@",error);
        if (fail) {
            fail(error);
        }
    } else {
        NSLog(@"添加数据成功");
        if (success) {
            success();
        }
    }
}

// 查询数据
- (void)readEntity:(NSArray *)sequenceKeys ascending:(BOOL)isAscending filterStr:(NSString *)filterStr success:(void(^)(NSArray *results))success fail:(void(^)(NSError *error))fail
{
    // 1.初始化一个查询请求
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    // 2.设置要查询的实体
    NSEntityDescription *desc = [NSEntityDescription entityForName:self.entityName inManagedObjectContext:self.context];
    request.entity = desc;
    // 3.设置查询结果排序
    if (sequenceKeys&&sequenceKeys.count>0) { // 如果进行了设置排序
        NSMutableArray *array = [NSMutableArray array];
        for (NSString *key in sequenceKeys) {
            /**
             *  设置查询结果排序
             *  sequenceKey:根据某个属性(相当于数据库某个字段)来排序
             *  isAscending:是否升序
             */
            NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:key ascending:isAscending];
            [array addObject:sort];
        }
        if (array.count>0) {
            request.sortDescriptors = array;// 可以添加多个排序描述器,然后按顺序放进数组即可
        }
    }
    // 4.设置条件过滤
    if (filterStr) { // 如果设置了过滤语句
        NSPredicate *predicate = [NSPredicate predicateWithFormat:filterStr];
        request.predicate = predicate;
    }
    // 5.执行请求
    NSError *error = nil;
    NSArray *objs = [self.context executeFetchRequest:request error:&error]; // 获得查询数据数据集合
    if (error) {
        if (fail) {
            fail(error);
        }
    } else{
        if (success) {
            success(objs);
        }
    }
}

// 更新数据
- (void)updateEntity:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
    NSError *error = nil;
    [self.context save:&error];
    if (error) {
        NSLog(@"删除失败:%@",error);
        if (fail) {
            fail(error);
        }
    } else {
        if (success) {
            success();
        }
    }

}

// 删除数据
- (void)deleteEntity:(NSManagedObject *)entity success:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
    // 传入需要删除的实体对象
    [self.context deleteObject:entity];
    // 同步到数据库
    NSError *error = nil;
    [self.context save:&error];
    if (error) {
        NSLog(@"删除失败:%@",error);
        if (fail) {
            fail(error);
        }
    } else {
        if (success) {
            success();
        }
    }
}
@end

```

结语:以上纯属个人摸索和个人总结,不对的地方忘指出。其实在实际开发中不仅仅是增删改查这么简单,有时候会出现APP已经发布,但是数据库后续改变了,这就涉及到数据库的迁移,以及一些数据安全问题和线程等都是比较深入的,有专研精神的可以自行搜索相关资料。互勉!!!

上一篇 下一篇

猜你喜欢

热点阅读