iOS中几种数据持久化方案

时间:2023-01-21 03:23:05

概念

所谓持久化就是将数据保存到硬盘中,使得应用重启或者机器重启后可以继续访问之前保存的数据。

方案

  • plist文件(属性列表)
    将某些特定的类,通过XML文件的方式保存在目录中。
    1. 获得文件路径

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

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

      NSArray *result = [NSArray arrayWithContentsOfFile:fileName];
      NSLog(@"%@", result);
  • preference(偏好设置)
    1. 获得NSUserDefaults文件

      NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    2. 向文件中写入内容

      [userDefaults setObject:@"myString" forKey:@"string"];
      [userDefaults setBool:YES forKey:@"bool"];
      [userDefaults setInteger:21 forKey:@"integer"];
    3. 立即同步

      [userDefaults synchronize];
    4. 读取文件

      NSString *name = [userDefaults objectForKey:@"string"];
      BOOL sex = [userDefaults boolForKey:@"bool"];
      NSInteger age = [userDefaults integerForKey:@"integer"];
  • NSKeyedArchiver(归档)
    1. 属性设置

      @interface Person : NSObject //2.设置属性
      @property (strong, nonatomic) UIImage *avatar;
      @property (copy, nonatomic) NSString *name;
      @property (assign, nonatomic) NSInteger age;
      @end

    2.实现协议方法

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

    //解档
    - (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:aCoder] 和 [super initWithCoder:aDecoder] 方法;

    3.使用

    需要把对象归档是调用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];

    需要从文件中解档对象就调用NSKeyedUnarchiver的一个工厂方法 unarchiveObjectWithFile: 即可。

    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];
    }
  • SQLite3
    SQLite3的使用还是比较麻烦,在一般开发过程中,使用的都是第三方开源库 FMDB
    1. 打开数据库

      NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"];
      FMDatabase *database = [FMDatabase databaseWithPath:path];
      if (![database open]) {
      NSLog(@"数据库打开失败!");
      }
    2. 更新

      //常用方法有以下3种:
      - (BOOL)executeUpdate:(NSString*)sql, ...
      - (BOOL)executeUpdateWithFormat:(NSString*)format, ...
      - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
      //示例
      [database executeUpdate:@"CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)"];
      //或者
      [database executeUpdate:@"INSERT INTO t_person(name, age) VALUES(?, ?)", @"Bourne", [NSNumber numberWithInt:42]];
    3. 查询

      查询方法有3种
      - (FMResultSet *)executeQuery:(NSString*)sql, ...
      - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
      - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
      查询示例
      //1.执行查询
      FMResultSet *result = [database executeQuery:@"SELECT * FROM t_person"];
      //2.遍历结果集
      while ([result next]) {
      NSString *name = [result stringForColumn:@"name"];
      int age = [result intForColumn:@"age"];
      }
    4. 线程安全
      • 创建队列

        FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
      • 使用队列

        [queue inDatabase:^(FMDatabase *database) {
        [database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_1", [NSNumber numberWithInt:1]];
        [database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_2", [NSNumber numberWithInt:2]];
        [database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_3", [NSNumber numberWithInt:3]];
        FMResultSet *result = [database executeQuery:@"select * from t_person"];
        while([result next]) {
        }
        }];
  • CoreData

    1. 创建项目的时候选择使用Core Data,项目创建成功后,会在AppDelegate类中自动添加相关代码,此外,还会自动生成一个数据模型文件。如果项目在创建的时候没有选择使用Core Data,但是在后面需要使用,那么需要手动的添加AppDelegate中的相关代码。此外还需要手动添加一个Data Model文件。
      在创建Data Model文件时需要注意文件名要与AppDelegate.m中managedObjectModel方法中提到的文件名称相匹配。

    2. 有了Data Model文件后,就可以在里面添加实体和关系,实际上就是向数据库中添加表格和建立表格之间的关联。
    3. 创建好实体后,可以通过添加NSManagedObject subclass文件,系统可以自动添加实体对应的数据模型类。
    4. 通过代码实现数据库的操作
      • 插入数据

        -(void)insert {       
        AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
        //1. 获得context
        NSManagedObjectContext *context = delegate.managedObjectContext;
        //2. 找到实体结构,并生成一个实体对象
        /* NSEntityDescription实体描述,也就是表的结构 参数1:表名字 参数2:实例化的对象由谁来管理,就是context */
        NSManagedObject *stu = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:context];
        NSManagedObject *class1 = [NSEntityDescription insertNewObjectForEntityForName:@"Classes" inManagedObjectContext:context];
        [class1 setValue:[NSNumber numberWithInt:1] forKey:@"c_id"];
        [class1 setValue:@"一班" forKey:@"c_name"];
        //3. 设置实体属性值
        [stu setValue:[NSNumber numberWithInt:1] forKey:@"s_id"];
        [stu setValue:@"jerehedu" forKey:@"s_name"];
        [stu setValue:class1 forKey:@"s_class"];
        //4. 调用context,保存实体,如果没有成功,返回错误信息
        NSError *error;
        if ([context save:&error]){
        NSLog(@"save ok");
        } else{
        NSLog(@"%@",error);
        }}
      • 查询全部数据

        -(void)selectAll {    
        AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
        NSManagedObjectContext *context = delegate.managedObjectContext;
        NSEntityDescription *stu = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:context];
        //构造查询对象
        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        [request setEntity:stu];
        //执行查询,返回结果集
        NSArray *resultAry = [context executeFetchRequest:request error:nil];
        //遍历结果集
        for (NSManagedObject *enity in resultAry) {
        NSLog(@"id=%i name=%@ class=%@",[[enity valueForKey:@"s_id"] intValue],[enity valueForKey:@"s_name"],[[enity valueForKey:@"s_class"] valueForKey:@"c_name"]);
        }}
      • 查询指定数据

        - (void)selectAll {    
        AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
        NSManagedObjectContext *context = delegate.managedObjectContext;
        NSEntityDescription *stu = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:context];
        //构造查询对象
        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        [request setEntity:stu];
        //执行查询,返回结果集
        NSArray *resultAry = [context executeFetchRequest:request error:nil];
        //遍历结果集
        for (NSManagedObject *enity in resultAry) {
        NSLog(@"id=%i name=%@ class=%@",[[enity valueForKey:@"s_id"] intValue],[enity valueForKey:@"s_name"],[[enity valueForKey:@"s_class"] valueForKey:@"c_name"]);
        }}
      • 删除指定数据

        - (void)delete{    
        //删除 先找到,然后删除
        AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
        NSManagedObjectContext *context = delegate.managedObjectContext;
        NSEntityDescription *stu = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:context];
        NSFetchRequest *request = [NSFetchRequest new];
        [request setEntity:stu];
        //构造查询条件,相当于where子句
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"s_id=%i",1];
        //把查询条件放进去
        [request setPredicate:predicate];
        //执行查询
        NSManagedObject *obj = [[context executeFetchRequest:request error:nil] lastObject];
        //删除
        if (obj) {
        [context deleteObject:obj]; [context save:nil];
        }
        [self selectAll];
        }