iOS第三方插件&库iOS开发学习iOS学习笔记

iOS数据持久化方案

2017-09-26  本文已影响0人  香橙柚子

持久化,就是将数据保存起来。使得下次APP启动的时候,可以继续访问。

在iOS开发中,我总结了一下,经常用到的几种数据存数存储方案。

沙盒

每个iOS程序都有一个独立的文件系统(存储空间),而且只能在对应的文件系统中进行操作,也就是默认情况下只能访问程序自己的目录,此区域被称为沙盒。
在介绍各种存储方法之前,我们必须了解一下沙盒的基本概念。

沙盒结构

如下:

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

顺便上传网上copy的图片:


沙盒结构
沙盒中相关路径

获取沙盒各个目录路径方法:

    // 获取沙盒根目录路径
    NSString *homeDir = NSHomeDirectory();
    
    // 获取Documents目录路径
    NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) firstObject];
    
    //获取Library的目录路径
    NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask,YES) lastObject];
    
    // 获取cache目录路径
    NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES) firstObject];

    // 获取tmp目录路径
    NSString *tmpDir =NSTemporaryDirectory();
    
    // 获取应用程序程序包中资源文件路径的方法:
    NSString *bundle = [[NSBundle mainBundle] bundlePath];

    NSLog(@"homeDir=%@ \n docDir=%@ \n libDir=%@ \n cachesDir=%@ \n tmpDir=%@ \n bundle=%@", homeDir,docDir, libDir, cachesDir, tmpDir, bundle);

开始进入正题

1. plist

plist文件是将某些特定的类,通过XML文件的方式保存在目录中。

可以被序列化的类型只有如下几种:

NSArray;
NSMutableArray;
NSDictionary;
NSMutableDictionary;
NSData;
NSMutableData;
NSString;
NSMutableString;
NSNumber;
NSDate;
使用
- (void)plist{
    //获取文件路径
    NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    NSString *fileName = [path stringByAppendingPathComponent:@"mygame.plist"];
    //存储
    NSArray *array = @[@"红色警戒", @"王者荣耀", @"穿越火线",@"英雄联盟",@"超级玛丽"];
    [array writeToFile:fileName atomically:YES];
    //读取
    NSArray *result = [NSArray arrayWithContentsOfFile:fileName];
    NSLog(@"%@", result);
}

2 偏好设置

-几乎每个App都使用有偏好设置,如用户名,密码,字体大小等,iOS提供了一套标准的解决方案来为应用提供这项功能

- (void)userDefaults{
    //1.获得NSUserDefaults文件
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
        
    //2.向文件中写入内容
    [userDefaults setObject:@"小明" forKey:@"name"];
    [userDefaults setBool:YES forKey:@"sex"];
    [userDefaults setInteger:21 forKey:@"age"];
    //2.1立即同步
    [userDefaults synchronize];
        
    //3.读取文件
    NSString *name = [userDefaults objectForKey:@"name"];
    BOOL sex = [userDefaults boolForKey:@"sex"];
    NSInteger age = [userDefaults integerForKey:@"age"];
        
    NSLog(@"%@, %d, %ld", name, sex, age);
}

3. 归解档(NSKeyedArchiver)

归档可以实现把自定义的对象存入文件中,解决了plist和偏好设置只能存储常用类型的致命缺陷,可以一次性取出对象的全部属性

只要遵循了NSCoding协议的对象都可以通过它实现序列化,然后进行归档。

创建一个类,给它一些属性,如果想将这个类保存到文件中,必须让它遵守<NSCoding>

@interface Dog : NSObject<NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *color;
@end

在该类内部实现归档解档方法,(将一个自定义对象保存到文件中或从文件中读取的时候就会调用下面两个方法)

@implementation Dog

//归档,告诉系统该对象的哪些属性需要存储
- (void)encodeWithCoder:(NSCoder *)aCoder {

   [aCoder encodeObject:self.name forKey:@"name"];
   [aCoder encodeInteger:self.age forKey:@"age"];
   [aCoder encodeObject:self.color forKey:@"color"];
}

//解档,告诉系统该对象的哪些属性可以获取
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {

   if (self = [super init]) {
       self.name = [aDecoder decodeObjectForKey:@"name"];
       self.age = [aDecoder decodeIntegerForKey:@"age"];
       self.color = [aDecoder decodeObjectForKey:@"color"];
   }
   return self; 
}
@end

归档和解档

NSString * filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"DogList"];
[NSKeyedArchiver archiveRootObject:dog toFile:filePath];
Dog *dog = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];

4.SQLite3

对于大量数据的存储,就要用到数据库,原生的SQLite3极其晦涩,不易懂,不易用。所有就有了第三方库FMDB
我们就简单介绍一下FMDB的用法。

在 FMDB 中有三个重要的类:

  1. FMDatabase:是一个提供 SQLite 数据库的类,用于执行 SQL 语句。
  2. FMResultSet:用在FMDatabase中执行查询的结果的类。
  3. FMDatabaseQueue:在多线程下查询和更新数据库用到的类。

建立一个Person类,进行存储

@property(nonatomic,copy) NSString *name;
@property(nonatomic,assign) NSInteger age;
@property(nonatomic,assign) NSInteger number;
#import "DataManager.h"
#import "FMDB.h"

@interface DataManager (){
    FMDatabase  *_db;
    FMDatabaseQueue *_queue;
}

@end

@implementation DataManager

static id _instance;
+ (instancetype)shared{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc]init];
    });
    return _instance;
}

// 初始化数据
- (void)initDataBase{
    // 获得Documents目录路径
    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    // 文件路
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"model5.sqlite"];
    _queue = [FMDatabaseQueue databaseQueueWithPath:filePath];
    // 实例化FMDataBase对象
    _db = [FMDatabase databaseWithPath:filePath];
    
    if ([_db open]) {//'person_id' VARCHAR(255)
        // 初始化数据表
        NSString *personSql = @"CREATE TABLE 'person' ('id' INTEGER PRIMARY KEY AUTOINCREMENT  NOT NULL ,'id' VARCHAR(255),'name' VARCHAR(255),'age' VARCHAR(255),'number'VARCHAR(255)) ";
        
        BOOL res = [_db executeUpdate:personSql];
        if (res) {
            NSLog(@"success to creating db table");
        } else {
            NSLog(@"error when creating db table");
        }
        [_db close];
        
    } else {
        NSLog(@"error when open db");
    }
}
 
// 添加一条数据
//和insertStudent思路不一样
- (void)addPerson:(Person *)person{
    [_db open];
    NSNumber *maxID = @(0);
    FMResultSet *res = [_db executeQuery:@"SELECT * FROM person "];
    //获取数据库中最大的ID
    while ([res next]) {
        if ([maxID integerValue] < [[res stringForColumn:@"id"] integerValue]) {
            maxID = @([[res stringForColumn:@"id"] integerValue] ) ;
        }
    }
    maxID = @([maxID integerValue] + 1);
    
    [_db executeUpdate:@"INSERT INTO person(id,name,age,number) VALUES(?,?,?,?)",maxID,person.name,@(person.age),@(person.number)];
    [_db close];
    
}


/// 添加一条数据
/// @param person person
- (void)insertStudent:(Person *)person{
    NSLog(@"%s", __func__);
    static int idx = 1;
    //    FMDatabase *db = [FMDatabase databaseWithPath:self.dbPath];
    if ([_db open]) {
        NSString *sql = @"insert into person (name, age, number) values(?, ?, ?) ";
        BOOL res = [_db executeUpdate:sql, person.name, @(person.age), @(person.number)];
        if (!res) {
            NSLog(@"error to insert data");
        } else {
            NSLog(@"success to insert data");
        }
        [_db close];
    }
}

/// 添加多条数据
/// @param persons persons
- (void)insertStudents:(NSArray <Person *> *)persons{
    for (Person *p in persons) {
        [self insertStudent:p];
    }
}

/// 更新一条数据
/// @param person person
- (void)updateStudent:(Person *)person{
    
    [_queue inDatabase:^(FMDatabase * _Nonnull db) {
        //更新某个学生的年龄数据
        BOOL result = [db executeUpdate:@"update t_person set age = ? where name = ? ;",@(person.age), person.name];
        if (result) {
            NSLog(@"更新学生数据 name = %@ age = %ld  成功",person.name, (long)person.age);
        } else {
            NSLog(@"更新学生数据 name = %@ age = %ld  失败!",person.name, (long)person.age);
        }
    }];
}

/// 删除一条数据
/// @param person person
- (void)deleteStudent:(Person *)person{
    [_queue inDatabase:^(FMDatabase * _Nonnull db) {
        BOOL result = [db executeUpdate:@"delete from t_person where name = ? and age = ? ;",person.name, @(person.age)];
        if (result) {
            NSLog(@"删除学生数据 name = %@ age = %ld  成功",person.name, (long)person.age);
        } else {
            NSLog(@"删除学生数据 name = %@ age = %ld  失败!",person.name, (long)person.age);
        }
    }];
}


/// 查询数据
- (NSArray *)queryStudents{
    __block NSMutableArray *persons = [NSMutableArray array];
    
    //inDatabase 内部是一个同步线程,所以在 block 执行完毕之前,查询方法不会被提前 return
    [_queue inDatabase:^(FMDatabase * _Nonnull db) {
        //查询年龄大于 20岁的学生数据, ASC为升序(默认), DESC 为降序
        FMResultSet *rs = [db executeQuery:@"select * from t_student where age > ? order by age ASC;",@(20)];
        //用 while
        while (rs.next) {
            NSString *name = [rs stringForColumn:@"name"];
            int age = [rs intForColumn:@"age"];
            int number = [rs intForColumn:@"number"];
            Person *person = [[Person alloc] init];
            person.name = name;
            person.age = age;
            person.number = number;
            [persons addObject:person];
        }
    }];
    [_db close];
    return persons.copy;
}

- (NSArray *)getAllPerson{
    NSLog(@"%s", __func__);
    __block NSMutableArray *persons = [NSMutableArray array];
    if ([_db open]) {
        NSString *sql = @"select *from person";
        FMResultSet *rs = [_db executeQuery:sql];
        while ([rs next]) {
            NSString *name = [rs stringForColumn:@"name"];
            int age = [rs intForColumn:@"age"];
            int number = [rs intForColumn:@"number"];
            Person *person = [[Person alloc] init];
            person.name = name;
            person.age = age;
            person.number = number;
            [persons addObject:person];
        }
    }
    [_db close];
    return persons.copy;
}
@end

5. CoreData

CoreData是苹果官方出品的一款用于存储的数据库,其底层也是封装的SQlite3,但是比SQLite3更加面向对象。

但是,说实话,使用起来并不是非常的友好。

上一篇 下一篇

猜你喜欢

热点阅读