iOS数据持久化

本文介绍了iOS开发中文件管理的沙盒机制及其目录结构,包括Documents、Library和Tmp等目录的用途。同时,详细讲解了属性列表、对象归档和SQLite数据库三种数据存储方式的特点及使用方法。

一、文件管理

1.1 iOS沙盒机制: iOS应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒,所以所有的非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文本文件 等。
• 每个应用程序都有⾃己的存储空间
• 应用程序不能翻过⾃己的围墙去访问别的存储空间的内容

• 应用程序请求的数据都要通过权限检测,假如不符合条件的话,不会被放⾏。

1.1、文件保存的目录结构

iOS的文件存储采用的是沙盒目录机制,没有SD卡保存的概念。一个APP安装成功后就会对应一个文件目录这个目录就叫沙盒。这个APP的所有文件都会保存在这个目录中,每个APP沙盒独立互相不能访问目录,沙盒目录中包含Documents、Library、Tmp三个子目录。结构如下:

对于Document文件夹目录路径的获取,API提供了这么一种方法:

directory:要查找的目录

domainMask:从哪个主目录中获取(通常是沙盒目录)

expandTilde: 是否返回完整的路径

NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSLog(@"%@",documents);
1.1.1 Library

Library:存储程序的默认设置或其它状态信息;(iTunes备份和恢复的时候会包含此目录) Library/Caches:存放缓存⽂件,iTunes不会备份此目录,此目录下文件不会在应⽤退出删除

获取Library目录路径:

    NSString *library = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
    NSLog(@"%@",library);
1.1.2 获取缓存目录Caches路径
    NSString *cacher = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSLog(@"%@",cacher);
1.1.3 tmp

提供一个即时创建临时⽂件的地方。(系统重启后会删除tmp里的所有文件)

获取Tem目录路径:

NSString *tem = NSTemporaryDirectory();
    NSLog(@"%@",tem);

 

二、属性列表

2.1 属性列表的基本概念

属性列表是一种明文的轻量级存储方式,其存储格式有多种,最常规格式为XML格式。在我们创建一个新的项目的时候,Xcode会自动生成一个info.plist文件用来存储项目的部分系统设置。plist只能用数组(NSArray)或者字典(NSDictionary)进行读取,由于属性列表本身不加密,所以安全性几乎可以说为零。因为,属性列表正常用于存储少量的并且不重要的数据。

2.2 使用NSUserDefaults保存为pist文件

在程序启动后,系统会自动创建一个NSUserDefaults的单例对象,我们可以获取这个单例来存储少量的数据,它会将输出存储在.plist格式的文件中。其优点是像字典一样的赋值方式方便简单,但缺点是无法存储自定义的数据。

获得NSUserDefaults系统提供的plist文件:

     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

设值:

    [userDefaults setObject:@100 forKey:@"id"];
    [userDefaults setBool:YES forKey:@"isLoad"];
    [userDefaults setDouble:3.8408 forKey:@"price"];

数据同步(即使更新数据):

[userDefaults synchronize];

取值:

    id value = [userDefaults objectForKey:@"id"];
    NSLog(@"%@",value);

修改:

    [userDefaults setObject:@2000 forKey:@"id"];
    [userDefaults synchronize];

三、对象归档

3.1 对象归档的基本概念

与属性列表相反,同样作为轻量级存储的持久化方案,数据归档是进行加密处理的,数据在经过归档处理会转换成二进制数据,所以安全性要远远高于属性列表。另外使用归档方式,我们可以将复杂的对象写入文件中,并且不管添加多少对象,将对象写入磁盘的方式都是一样的。

使用NSKeyedArchiver对自定义的数据进行序列化,并且保存在沙盒目录下。使用这种归档的前提是让存储的数据模型遵守NSCoding协议并且实现其两个协议方法。(当然,如果为了更加安全的存储,也可以遵守NSSecureCoding协议,这是iOS6之后新增的特性)

• 概述: 对象归档是将对象归档以⽂文件的形式保存到磁盘中(也称为序列化,持久化),使用的时候读取该⽂文件的保存路径读取⽂文件的内容(也称为接档,反序列化), 对象归档的⽂文件是保密的,在磁盘上⽆无法查看⽂文件中的内容,⽽而属性列表是明⽂文的,可 以查看。

3.2 对象归档的方式

  • 简单归档
  • 对多个对象进⾏归档
  • 对自定义对象进⾏归档

归档类NSKeyedArchiver和反归档类NSKeyedUnarchiver总与NSData关联在一起。NSData封装了字节数据的缓存类,提供类读写数据的方法,具体如下所示:

dataWithContentsOfFile:他是静态工厂方法,用于从文件读取数据来创建NSData对象,

dataWithContentsOfFile:options:error:它是静态工厂方法,用于从文件读取数据来创建NSData对象,options参数是指定读取数据选项,error参数是返回读取数据的错误。

initWithContentsOfFile:options:error:它是实例构造函数,同上一个方法一样。

writeToFile:options:error:将NSData对象写入文件,atomically参数是否写入辅助文件,NO时数据直接写入目标文件路径,ture时数据写入辅助文件,写入成功后将辅助文件路径改名为目标文件路径。当目标文件已经存在时,atomically设置为false,可以防止系统崩溃所导致旧文件的破坏。

归档(编码保存):将数据以二进制的形式保存,常用的NSString、NSArray、NSDictionary.... 可以直接归档保存,因为它们都实现了 <NSCoding> 协议 : NSKeyedArchiver 归档代码:

    BOOL isSuccess = [NSKeyedArchiver archiveRootObject:user toFile:filePath];

归档过程是使用NSKeyedArchiver对象归档数据,具体过程为:首先将归档数据写入NSData对象,最后再将NSData对象写入归档文件,反归档过程是从归档文件中读取NSData对象,再利用NSKeyedUnarchiver对象从NSData对象中反归档数据。

归档协议方法实现:

#pragma mark - <NSCoing> 对属性编码和解码
// 编码
- (void)encodeWithCoder:(NSCoder *)aCoder {

    [aCoder encodeObject:_userName forKey:@"userName"];
    [aCoder encodeObject:_password forKey:@"password"];
}


// 解码
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {

    self = [super init];
    if (self ) {       
        self.userName = [aDecoder decodeObjectForKey:@"userName"];
        self.password = [aDecoder decodeObjectForKey:@"password"];
    }

    return self;
}

解归档(读取解码): NSKeyedUnarchiver:

    id object = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    User *user_ = (User *)object;

四、SQLite数据库

  • SQLite是一个轻量级、跨平台的小型数据库,其拥有可移植性高、有着和MySql几乎相同的数据库语句以及无需服务器即可使用的优点:

     1、可以存储大量的数据,存储和检索的速度非常快;

     2、能对数据进行大量的聚合,这样比起使用对象来进行这些操作要快。

当然,它也具有明显的缺点:

    1、它没有提供数据库的创建方式;

   2、它基于C语言框架设计,没有面向对象的API,所以使用起来比较麻烦;

   3、复杂的数据模型的数据建表相对而言比较麻烦。

当然,我们也可以使用基于SQLite封装的开源数据库FMDB来减少使用SQLite的工作量

4.1 创建数据库


    // 1、创建数据库指针,相当于创建一个数据库对象。
    sqlite3 *_sqlDB;

    // 2、创建数据库保存的路径
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *dataPath = [docPath stringByAppendingFormat:@"/%@.sqlite",dbName];

    // 3、打开数据库
    int result = sqlite3_open([dataPath UTF8String], &_sqlDB);
    if (result == SQLITE_OK) {

        NSLog(@"创建/打开成功!");
    } else {

        NSLog(@"创建/打开失败!");

        // 4、关闭数据库
        sqlite3_close(_sqlDB);
    }

代码解析

1、数据库扩展名称为.sqlite

2、sqlite3_open()函数包含两个参数,第一个为数据库的路径因为参数的类型是 char *类型,所有要用[dataPath UTF8String]进行类型转换,第二个为数据库sqlite3指针的地址。

3、如果函数的返回值等于 SQLITE_OK(宏) 表示成功。

4、数据库打开失败或者使用完后都要调用sqlite3_close()函数,关闭数据库释放资源。

4.2 创建数据库表

#pragma mark - 创建数据库表
- (void)createTable {
    
    NSString *sql = @"CREATE TABLE IF NOT EXISTS User('userID' TEXT, 'userName' TEXT, 'userAge' INTEGER)";
    
    // sqlite3_exec() 执行 sql语句 函数
    int result = sqlite3_exec(_sqliteDB,[sql UTF8String],NULL,NULL,NULL);
    
    if (result == SQLITE_OK) {
        
        NSLog(@"创建表成功");
    } else {
        
        NSLog(@"创建失败");
    }
    
}

4.3 插入数据 

#pragma mark - 数据库表中插入数据
- (void)insertValues {
    
    NSString *userID = @"100015";
    NSString *userName = @"李杨";
    int age = 20;
    
    NSString *sql = [NSString stringWithFormat:@"INSERT INTO User('userID','userName','userAge') VALUES('%@','%@',%d)",userID,userName,age];
    
    // sqlite3_exec() 执行 sql 函数
    int result = sqlite3_exec(_sqliteDB,[sql UTF8String],NULL,NULL,NULL);
    
    if (result == SQLITE_OK) {
        
        NSLog(@"插入数据成功");
    } else {
        
        NSLog(@"失败");
    }
}

4.4 添加字段 (列)

#pragma mark - 添加字段(列)
- (void)addColumn {
    
    NSString *columnName = @"userSex TEXT";
    NSString *sql = [NSString stringWithFormat:@"ALTER TABLE User ADD COLUMN %@",columnName];
    
    // sqlite3_exec() 执行 sql 函数
    int result = sqlite3_exec(_sqliteDB,[sql UTF8String],NULL,NULL,NULL);
    
    if (result == SQLITE_OK) {
        
        NSLog(@"添加字段成功");
    } else {
        
        NSLog(@"失败");
    }
    
}

代码解析:

1、sql语句:INSERT INTO Person (字段1,字段2...) VALUES (数据1,数据2...)"表示在Person表里的 pID,pName,pSex,pTel 字段中,分别插入对应的值。

2、也sqlite3_exec()执行sql语句,非查询的sql语句都用这个函数执行

4.5 更新数据 

#pragma mark - 更新数据
- (void)updateValues {
    
    NSString *userSex = @"男";
    NSString *userName = @"电风扇";
    NSString *sql = [NSString stringWithFormat:@"UPDATE User SET userSex = '%@' WHERE userName = '%@'",userSex,userName];
    
    // sqlite3_exec() 执行 sql 函数
    int result = sqlite3_exec(_sqliteDB,[sql UTF8String],NULL,NULL,NULL);
    
    if (result == SQLITE_OK) {
        
        NSLog(@"修改数据成功成功");
    } else {
        
        NSLog(@"失败");
    }
}

4.6 删除数据 

#pragma mark - 删除数据
- (void)deleteRow {
    
    NSString *userName = @"电风扇";
    NSString *sql = [NSString stringWithFormat:@"DELETE FROM User WHERE userName = '%@'",userName];
    
    // sqlite3_exec() 执行 sql 函数
    int result = sqlite3_exec(_sqliteDB,[sql UTF8String],NULL,NULL,NULL);
    
    if (result == SQLITE_OK) {
        
        NSLog(@"删除数据成功");
        
    } else {
        
        NSLog(@"失败");
    }
    
}

 4.7 查询数据


#pragma mark - 查询数据
- (void)selectDatas {
    
    //1、创建sql语句结构体指针(可以理解为sql语句对象)
    sqlite3_stmt *_stmt;
    
    NSString *key = @"张";
    NSString *sql = [NSString stringWithFormat:@"SELECT *FROM User WHERE userName LIKE '%%%@%%'",key];
    
    
    // 2、编译查询语句
    int result = sqlite3_prepare_v2(_sqliteDB,[sql UTF8String],-1,&_stmt,NULL);
    if (result == SQLITE_OK) {
        NSLog(@"sql语句编译通过");
        
         NSMutableArray *users = [NSMutableArray array];
        
        // 3、执行查询语句,== SQLITE_ROW 表示还有下一条数据
        while (sqlite3_step(_stmt) == SQLITE_ROW) {
            
            /* 4、获取每一行数据对应的字段(列)
             
             sqlite3_column_text() 对应 TEXT 类型
             sqlite3_column_int() 对应 INTERER类型
            */
            char *userID = (char *)sqlite3_column_text(_stmt,0);
            char *userName = (char *)sqlite3_column_text(_stmt,1);
            int userAge = sqlite3_column_int(_stmt,2);
            char *userSex = (char *)sqlite3_column_text(_stmt,3);
            
            NSString *userID_str = [NSString stringWithUTF8String:userID];
            NSString *userName_str = [NSString stringWithUTF8String:userName];
            NSString *userSex_str = [NSString stringWithUTF8String:userSex];
            
            // 把查询到的数据保存为 model 对象
            User *user = [[User alloc] init];
            user.userID = userID_str;
            user.userName = userName_str;
            user.userSex = userSex_str;
            user.userAge = userAge;
            [users addObject:user];
            
            NSLog(@"%@, %@, %d,%@",userID_str,userName_str,userAge,userSex_str);
            }
         } else {
         NSLog(@"失败");
    }
}

代码解析:

1、sql语句:SELECT * FROM 表名 WHERE 查询条件,表示从Person表格中查询pAge大于20的所有行。

2、sqlite3_stmt 相当于sql语句的对象,执行语句、获取查询到的取都是对这个对象进行操作。

3、sqlite3_prepare_v2(),sql语句预处理操作,在执行语句前做的事相当于编译 sql 语句。包含五个参数,第一个数据库指针,第二个sql语句,第三个能从sql语句中获取的最大字节数,使用-1表示整个sql语句。第四个sqlite3_stmt对象地址,第五个指向未使用部分的 sql 语句的指针,如果为前面为-1就使用NULL。

4、sqlite3_step(),执行 sql 语句,将编译好的sql语句执行,这个函数可能会多次执行,因为查询的数据可能有多条,所有要一个循环语句一起使用。循环条件是sqlite3_step(stmt) == SQLITE_ROW 表示还有其他行数据。

5、sqlite3_column_text(stmt, 0),获取查询到的一行结果中对应列的数据,text表示字符串类型 返回char *类型,0表示获取第0列的数据。这个函数还有int、double等类型,返回值得类型也对应。选择列对应的数据类型的函数。

6、使用数据库查询到的数据通常要保存到 mode层中,方便使用,所有创建了一个Person对象保存对应的数据。

7、sqlite3_finalize(),使用完查询对象后要释放。

转载于:https://my.oschina.net/nieqing/blog/740922

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值