IOS | MACiOS开发 oc中重要知识点iOS学习小集

iOS数据持久化方式

2016-11-10  本文已影响400人  向阳的向日葵花

iOS数据持久化方式

一、文件

NSString *filePath = [[self getDocumentPath] stringByAppendingString:@"fileTest.txt"];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"798293@qq.com", @"email", @"787907@qq.com", @"emailDisplay", nil];
[dictionary writeToFile:filePath atomically:YES];

二、归档

归档(又名序列化),把对象转为字节码,以文件的形式存储到磁盘上,程序运行过程中或者再次重新打开程序的时候,可以通过解归档(返序列化)还原这些对象。

[NSKeyedArchiver archiveRootObject:obj toFile:appSettingPath];会调用对象的encodeWithCoder方法
- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:_name forKey:kAddressCardName];
    [aCoder encodeObject:_emailObj forKey:kAddressCardEmail];
    [aCoder encodeInteger:_salary forKey:kAddressCardSalary];
}

[NSKeyedUnarchiver unarchiveObjectWithFile:appSettingPath];会调用对象的initWithCoder方法
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
    _name = [aDecoder decodeObjectForKey:kAddressCardName];
    _emailObj = [aDecoder decodeObjectForKey:kAddressCardEmail];
    _salary = [aDecoder decodeIntegerForKey:kAddressCardSalary];
    return self;
}

三、属性列表

NSUserDefaults适合存储轻量级的本地数据,一些简单的数据(NSString类型的)例如密码,网址等,NSUserDefaults肯定是首选,但是如果我们自定义了一个对象,对象保存的是一些信息,这时候就不能直接存储到NSUserDefaults了。他的方便之处在于不用声明太多的变量来存储不同的数据,一个NSUserDefaults就可以搞定,他是应用程序域的,能让我们进行更加方便的使用。

原理:NSUserDefaults类提供了与默认数据库相交互的编程接口。其实它存储在应用程序的一个plist文件里,路径为沙盒Document目录平级的/Library/Prefereces里。如果将默认数据库比喻为SQL数据库,那么NSUserDefaults就相当于SQL语句。

NSUserDefaults支持的数据类型有:NSNumber(NSInteger、float、double),NSString,NSDate,NSArray,NSDictionary,BOOL,NSData

user defaults数据库中其实是由多个层级的域组成的,当你读取一个键值的数据时,NSUserDefaults从上到下透过域的层级寻找正确的值,不同的域有不同的功能,有些域是可持久的,有些域则不行。

注意:

偏好设置是专门用来保存应用程序的配置信息的,一般不要在偏好设置中保存其他数据。

如果没有调用synchronize方法,系统会根据I/O情况不定时刻地保存到文件中。所以如果需要立即写入文件的就必须调用synchronize方法。

偏好设置会将所有数据保存到同一个文件中。即preference目录下的一个以此应用包名来命名的plist文件。

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"jack" forKey:@"firstName"];
[defaults setInteger:10 forKey:@"Age"];
[defaults synchronize];

四、SQLite

SQLite数据库的几个特点:

在iOS中操作SQLite数据库可以分为以下几个步骤:

打开数据库
    if(sqlite3_open(filePath.UTF8String, &_database) == SQLITE_OK)
    {
        NSLog(@"数据库打开成功");
    }
    
执行无返回结果的SQL
    if(sqlite3_exec(_database, sql.UTF8String, NULL, NULL, &error) != SQLITE_OK)
    {
        NSLog(@"执行SQL语句过程中发生错误,错误信息:%s", error);
    }
    
执行有返回结果的SQL
    sqlite3_stmt *stmt;
    if(sqlite3_prepare_v2(_database, sql.UTF8String, -1, &stmt, NULL) == SQLITE_OK)
    {
        while(sqlite3_step(stmt) == SQLITE_ROW)
        {
            int columnCount = sqlite3_column_count(stmt);
            NSMutableDictionary *dic = [NSMutableDictionary dictionary];
            for(int i = 0; i < columnCount; i++)
            {
                const char *name = sqlite3_column_name(stmt, i);
                const unsigned char *value = sqlite3_column_text(stmt, i);
                [dic setValue:[NSString stringWithUTF8String:(const char *)value] forKey:[NSString stringWithUTF8String:name]];
            }
            [rows addObject:dic];
        }
        sqlite3_finalize(stmt);
    }

五、CoreData

Core Data是iOS5之后才出现的一个框架,它提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite数据库文件中,也能够将保存在数据库中的数据还原成OC对象。在此数据操作期间,我们不需要编写任何SQL语句

简介

Core Data是个框架(并不是数据库哦),它使开发者可以把数据当做对象来操作,而不必在乎数据在磁盘中的存储方式。对于iOS程序员来说,这很有用,因为我们已经可以通过代码非常熟悉的操作对象了。由Core Data 所提供的数据对象叫做托管对象(Managed Object),而Core Data本身则位于你的应用程序和持久化存储区(Persistent store)之间。为了把数据从托管对象映射到持久化存储区中,Core Data 需要使用托管对象模型。所有的托管对象都必须位于托管对象上下文(Managed object context)里面,而托管对象上下文又位于高速的易失性存储器里面,也就是位于RAM中。

为什么需要有托管对象上下文呢?原因之一就是在磁盘与RAM之间传输数据时会有开销。磁盘读写速度比RAM慢的多,所以不应该频繁地访问它。有了托管对象上下文,就可以非常迅速地获取到了。但它的缺点在于,开发者必须在托管对象上下文上面定期调用save方法,以将变更后的数据写回磁盘。托管对象上下文的另一个功能是记录开发者对托管对象所对的修改,以提供完整的撤销和重做支持。

上边我们对Core Data简单的介绍了一下。接下来我们需要对CoreData中的重要的名词做一解释。

持久化存储协调器NSPersistentStoreCoordinator

持久化存储协调器(persistent store coordinator)里面包含一份持久化存储区,而存储区里面又含有数据表里面的若干行数据。设置持久化存储协调器的时候,我们通常选用SQLite数据库作为持久化存储区。另外,也可以选用Binary、XML、或In-Memory等形式的持久化存储区。但要注意的是,Binary和XML格式的存储区是Atomic,也就是说,即便你只想修改少量的数据,在保存的时候也依然需要把整个文件都写入磁盘。

同一个持久化存储协调器可以有多个持久化存储区。把CoreData与iCloud相集成的时候,就可能会出现这样的情况。我们可以把不属于iCloud的数据放在一个存储区里面,而把属于iCloud的数据放在另外一个存储区里面,这样既能节省网络宽带,又能节省iCloud存储空间。

即便你有两个持久化存储区,也不意味着必须使用两种对象图。CoreData的模型配置允许开发者使用多个独立的存储区,但却采用同一套对象图。在设置CoreData的模型配置选项时,可以指明对象图里面的某一部分属于哪一个持久化存储区。

要想创建持久化存储区,需生成NSPersistentStore;要想创建持久化存储协调器,需生成NSPersistentStoreCoordinator类的实例。

托管对象模型NSManagedObjectModel

托管对象模型它位于持久化存储协调器和托管对象上下文之间。顾名思议,托管对象模型是描述数据结构的模型或者图示,而托管对象正是以它为基础产生出来的。可以用Xcode来配置实体(Entity)及实体之间的关系。实体本身并不包含数据,它们只是规定了基于该实体的托管对象具有何种特性。实体也有属性,属性的数据类型可以是整数,字符串,或者日期等。

要想创建托管对象模型,需要生成NSManagedObjectModel类的实例

托管对象上下文NSManagedObjectContext

托管对象上下文中可包含多个托管对象。托管对象上下文负责管理其中对象的生命期,并且负责提供许多强大的功能。

托管对象上下文也可以不止有一个,有时我们需要在后台处理任务(比方说把数据保存到磁盘或者导入数据),这种情况下可以采用多个上下文。加入在前台上下文上面调用Save,那么用户界面就可能会有卡顿现象,尤其当数据变化较大的时候更是如此。要想避免这个问题,有个简单的办法就是只在用户按下手机的Home键时才去调用Save,这时应用程序会转入到后台。还有个稍微复杂的但却很灵活的办法,就是采取两个托管对象上下文。请记住,托管对象上下文是存放在高速内存里面的。你可以配置其中一个上下文,那么就可以将后台上下文中的数据异步存入磁盘。这种分段式的做法可以确保磁盘写入操作不会影响用户界面的流畅度。

要想创建托管对象上下文,需要生成NSManagedObjectContext类的实例

coreData简单创建流程

模型文件操作

1.1 创建模型文件,后缀名为.xcdatamodeld。创建模型文件之后,可以在其内部进行添加实体等操作(用于表示数据库文件的数据结构)

1.2 添加实体(表示数据库文件中的表结构),添加实体后需要通过实体,来创建托管对象类文件。

1.3 添加属性并设置类型,可以在属性的右侧面板中设置默认值等选项。(每种数据类型设置选项是不同的)

1.4 创建获取请求模板、设置配置模板等。

1.5 根据指定实体,创建托管对象类文件(基于NSManagedObject的类文件)

实例化上下文对象

2.1 创建托管对象上下文(NSManagedObjectContext)

2.2 创建托管对象模型(NSManagedObjectModel)

2.3 根据托管对象模型,创建持久化存储协调器(NSPersistentStoreCoordinator)

2.4 关联并创建本地数据库文件,并返回持久化存储对象(NSPersistentStore)

2.5 将持久化存储协调器赋值给托管对象上下文,完成基本创建。

Mou icon
    // 从应用程序包中加载模型文件
    NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
    // 传入模型对象,初始化持久化存储协调器
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    // 构建SQLite数据库文件的路径
    NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSURL *url = [NSURL fileURLWithPath:[docs stringByAppendingString:@"person"]];
    // 添加持久化存储器,用sqlite作为存储库
    NSError *error = nil;
    NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
    if(store == nil)
    {
        [NSException raise:@"添加数据库错误" format:@"%@", [error localizedDescription]];
    }
    // 创建托管对象上下文
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    context.persistentStoreCoordinator = psc;
    NSManagedObject *card = [NSEntityDescription insertNewObjectForEntityForName:@"Card" inManagedObjectContext:context];
    [card setValue:@"4768558865" forKey:@"no"];
    [person setValue:card forKey:@"card"];
    // 利用上下文对象,将数据同步到持久化存储库
    NSError *errorSave = nil;
    BOOL sucess = [context save:&errorSave];
查询数据

NSFetchRequest

在执行fetch操作前,可以给NSFetchRequest设置一些参数,这些参数包括谓词、排序等条件,下面是一些基础的设置。

MOC执行fetch操作后,获取的结果是以数组的形式存储的,数组中存储的就是托管对象。NSFetchRequest提供了参数resultType,参数类型是一个枚举类型。通过这个参数,可以设置执行fetch操作后返回的数据类型。

NSPredicate

在iOS开发过程中,很多需求都需要用到过滤条件。例如过滤一个集合对象中存储的对象,可以通过Foundation框架下的NSPredicate类来执行这个操作。

CoreData中可以通过设置NSFetchRequest类的predicate属性,来设置一个NSPredicate类型的谓词对象当做过滤条件。通过设置这个过滤条件,可以只获取符合过滤条件的托管对象,不会将所有托管对象都加载到内存中。这样是非常节省内存和加快查找速度的,设计一个好的NSPredicate可以优化CoreData搜索性能。

// 从数据库查询数据
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    request.entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
    NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];
    request.sortDescriptors = [NSArray arrayWithObject:sort];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@", @"MJ55*"];
    request.predicate = predicate;
    // 执行请求
    NSError *errorFetch = nil;
    NSArray *objs = [context executeFetchRequest:request error:&errorFetch];
    if(errorFetch)
    {
        [NSException raise:@"查询错误" format:@"%@", [errorFetch localizedDescription]];
    }
总结

NSPersistentStoreCoordinator有四种可选的持久化存储方案,用得最多的是SQLite的方式。其中Binary和XML这两种方式,在进行数据操作时,需要将整个文件加载到内存中,这样对内存的消耗是很大的。

在coredata中所有的托管对象被创建出来后,都是关联着context对象的,所以在对象进行任何操作后,都会被记录在context中,在最后调用context的save方法后,context会将操作交给coordinator去处理,coordinator将会将这个存储任务指派给NSPersistentStore对象。

例子的地址:https://github.com/FangyingLu/storeSummarize

上一篇下一篇

猜你喜欢

热点阅读