美文网首页
iOS中的数据持久化方式

iOS中的数据持久化方式

作者: 凌晨四点的洛杉矶 | 来源:发表于2015-12-26 14:20 被阅读64次

    原文: 点击查看

    数据持久化 ,就是将数据保存起来,以便应用程序或设备重启后仍然可以访问

    • plist 文件 (属性列表)
    • preference (偏好设置)
    • NSKeyedArchiver (归档)
    • SQLite
    • CoreData (它提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite数据库文件中,也能够将保存在数据库中的数据还原成OC对象)

    沙盒

    只允许本程序访问的目录

    目录结构:

    "应用程序包"
    Documents
    Library
        Caches
        Preferences
    tmp
    

    应用程序包

    这里面存放的是应用程序的源文件,包括资源文件和可执行文件

      NSLog(@"%@",  [[NSBundle mainBundle] bundlePath]);
    

    Documents

    最常用的目录,iTunes同步该应用时会同步此文件夹中的内容,适合存储重要数据。

      NSLog(@"%@", NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject);
    

    Library/Caches

    iTunes不会同步此文件夹,适合存储体积大,不需要备份的非重要数据。

      NSLog(@"%@", NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject);
    

    Library/Preferences

    iTunes 会同步此文件夹, 保存用户偏好设置

      NSLog(@"%@", NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject);
    

    tmp

    iTunes不会同步此文件夹,系统可能在应用没运行时就删除该目录下的文件,所以此目录适合保存应用中的一些临时文件,用完就删除。

      NSLog(@"%@", NSTemporaryDirectory());
    

    plist 文件

    将某些特定的类通过 xml 方式存在目录中

    可被序列化的类型:

    • NSArray;
    • NSMutableArray;
    • NSDictionary;
    • NSMutableDictionary;
    • NSData;
    • NSMutableData;
    • NSString;
    • NSMutableString;
    • NSNumber;
    • NSDate;

    获得文件路径

        NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
        NSString *fileName = [path stringByAppendingPathComponent:@"123.plist"];
    

    存储

    NSArray *array = @[@"123", @"456", @"789"];
    [array writeToFile:fileName atomically:YES];
    

    读取

    NSArray *result = [NSArray arrayWithContentsOfFile:fileName];
    NSLog(@"%@", result);
    
    • 只有以上列出的类型才能使用 plist 文件存储。
    • 存储时使用writeToFile: atomically:方法。 其中atomically表示是否需要先写入一个辅助文件,再把辅助文件拷贝到目标文件地址。这是更安全的写入文件方法,一般都写YES
    • 读取时使用arrayWithContentsOfFile:方法。

    Preference

    //1.获得NSUserDefaults文件
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    //2.向文件中写入内容
    [userDefaults setObject:@"AAA" forKey:@"a"];
    [userDefaults setBool:YES forKey:@"sex"];
    [userDefaults setInteger:21 forKey:@"age"];
    //2.1立即同步
    [userDefaults synchronize];
    //3.读取文件
    NSString *name = [userDefaults objectForKey:@"a"];
    BOOL sex = [userDefaults boolForKey:@"sex"];
    NSInteger age = [userDefaults integerForKey:@"age"];
    NSLog(@"%@, %d, %ld", name, sex, age);
    
    • 偏好设置是专门用来保存应用程序的配置信息的,一般不要在偏好设置中保存其他数据。
    • 如果没有调用synchronize方法,系统会根据I/O情况不定时刻地保存到文件中。所以如果需要立即写入文件的就必须调用synchronize方法。
    • 偏好设置会将所有数据保存到同一个文件中。即preference目录下的一个以此应用包名来命名的plist文件。

    归档 NSKeyedArchiver

    因为前两者都有一个致命的缺陷,只能存储常用的类型。但只要遵循了NSCoding协议的对象都可以通过它实现序列化。由于决大多数支持存储数据的Foundation和Cocoa Touch类都遵循了NSCoding协议, 所以归档可以实现把自定义的对象存放在文件中。

    NSCoding 协议

    NSCoding协议声明了两个方法,这两个方法都是必须实现的。一个用来说明如何将对象编码到归档中,另一个说明如何进行解档来获取一个新对象。

    • 遵循 NSCoding 协议
      //1.遵循NSCoding协议 
      @interface Person : NSObject   //2.设置属性
      @property (strong, nonatomic) UIImage *avatar;
      @property (copy, nonatomic) NSString *name;
      @property (assign, nonatomic) NSInteger age;
      @end
    
    • 实现协议方法
    // 反归档
      - (id)initWithCoder:(NSCoder *)aDecoder {
          if ([super init]) {
              self.avatar = [aDecoder decodeObjectForKey:@"avatar"];
              self.name = [aDecoder decodeObjectForKey:@"name"];
              self.age = [aDecoder decodeIntegerForKey:@"age"];
          }
          return self;
      }
      // 归档
      - (void)encodeWithCoder:(NSCoder *)aCoder {
          [aCoder encodeObject:self.avatar forKey:@"avatar"];
          [aCoder encodeObject:self.name forKey:@"name"];
          [aCoder encodeInteger:self.age forKey:@"age"];
      }
    

    如果需要归档的类是某个自定义类的子类时,就需要在归档和解档之前先实现父类的归档和解档方法。即 super encodeWithCoder:aCodersuper initWithCoder:aDecoder 方法;

    • 归档
      需要把对象归档时调用NSKeyedArchiver的工厂方法 archiveRootObject: toFile:方法。
      NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
      Person *person = [[Person alloc] init];
      person.avatar = self.avatarView.image;
      person.name = self.nameField.text;
      person.age = [self.ageField.text integerValue];
      [NSKeyedArchiver archiveRootObject:person toFile:file];
    
    • 反归档
      NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
      Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
      if (person) {
         self.avatarView.image = person.avatar;
         self.nameField.text = person.name;
         self.ageField.text = [NSString stringWithFormat:@"%ld", person.age];
      }
    

    ** 注意 **

    • 必须遵循并实现NSCoding协议
    • 保存文件的扩展名可以任意指定
    • 继承时必须先调用父类的归档解档方法

    SQLite 3

    之前的所有存储方法,都是覆盖存储。如果想要增加一条数据就必须把整个文件读出来,然后修改数据后再把整个内容覆盖写入文件。所以它们都不适合存储大量的内容。

    SQLite 中可以将数据分为以下类型:

    • integer : 整数
    • real : 实数(浮点数)
    • text : 文本字符串
    • blob : 二进制数据,比如文件,图片之类的

    ** 注: ** 主键必须设置成integer.

    1. 导入依赖库

    导入 libsqlite3.dylib 并导入主头文件。

    2. 创建数据库并打开

    操作数据库之前必须先指定数据库文件和要操作的表,所以使用SQLite3,首先要打开数据库文件,然后指定或创建一张表。

    /**
    *  打开数据库并创建一个表
    */
    - (void)openDatabase {
       //1.设置文件名
       NSString *filename = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"];
       //2.打开数据库文件,如果没有会自动创建一个文件
       NSInteger result = sqlite3_open(filename.UTF8String, &_sqlite3);
       if (result == SQLITE_OK) {
           NSLog(@"打开数据库成功!");
           //3.创建一个数据库表
           char *errmsg = NULL;
           sqlite3_exec(_sqlite3, "CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)", NULL, NULL, &errmsg);
           if (errmsg) {
               NSLog(@"错误:%s", errmsg);
           } else {
               NSLog(@"创表成功!");
           }
       } else {
           NSLog(@"打开数据库失败!");
       }
    }
    

    3. 执行命令

    使用 sqlite3_exec() 方法可以执行任何SQL语句,比如创表、更新、插入和删除操作。但是一般不用它执行查询语句,因为它不会返回查询到的数据。

    // 往表中插入数据
     NSString *sql = [NSString stringWithFormat:@"INSERT INTO t_person (name, age) VALUES('%@', '%ld')", nameStr, age];
      char *errmsg = NULL;
      sqlite3_exec(_sqlite3, sql.UTF8String, NULL, NULL, &errmsg);
    

    4. 查询命令

    • sqlite3_prepare_v2() : 检查sql的合法性
    • sqlite3_step() : 逐行获取查询结果,不断重复,直到最后一条记录
    • sqlite3_coloum_xxx() : 获取对应类型的内容,iCol对应的就是 SQL语句中字段的顺序,从0开始。根据实际查询字段的属性,使用sqlite3_column_xxx取得对应的内容即可。
    • sqlite3_finalize() : 释放stmt
    /**
    *  从表中读取数据到数组中
    */
    - (void)readData {
       NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1000];
       char *sql = "select name, age from t_person;";
       sqlite3_stmt *stmt;
       NSInteger result = sqlite3_prepare_v2(_sqlite3, sql, -1, &stmt, NULL);
       if (result == SQLITE_OK) {
           while (sqlite3_step(stmt) == SQLITE_ROW) {
               char *name = (char *)sqlite3_column_text(stmt, 0);
               NSInteger age = sqlite3_column_int(stmt, 1);
               //创建对象
               Person *person = [Person personWithName:[NSString stringWithUTF8String:name] Age:age];
               [mArray addObject:person];
           }
           self.dataList = mArray;
       }
       sqlite3_finalize(stmt);
    }
    

    CoreData

    CoreData 参考这位大神的简书, 写得非常详细.
    链接: 我要娶你做我的CoreData! 原文

    相关文章

      网友评论

          本文标题:iOS中的数据持久化方式

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