iOS开发 - 利用SQLite和归档实现一个完美的数据持久化方案

版权声明:欢迎大家积极分享!交流。关注我~ https://blog.csdn.net/qinqi376990311/article/details/81538142

iOS开发 - 利用SQLite和归档实现一个完美的数据持久化方案

数据持久化方案,可能很多人能想到,SQLite、CoreData、各种方案。有利有弊。我想到了一个比较完美的解决方案。

要用到两个第三方:

  • MJExtension (主要为了实现 NSCoding 协议)
  • FMDB (主要为了方便操作SQLite)

好,开始~现在创建一个 Model 遵循 NSCoding 协议 ,这里我以一个用户模型作为示例:
SCUser.h

#import <Foundation/Foundation.h>

typedef NS_ENUM(NSUInteger, UserSex) {
    UserSexPrivary = 0, //保密
    UserSexMale,    //男
    UserSexFemale  //女
};

@interface SCUser : NSObject <NSCoding>

@property (nonatomic, copy) NSString *userId;
@property (nonatomic, copy) NSString *username;
@property (nonatomic, copy) NSString *mobile;
@property (nonatomic, copy) NSString *nickname;
@property (nonatomic, assign) UserSex sex;
@property (nonatomic, copy) NSString *avatar;

@end

SCUser.m

#import "SCUser.h"

@implementation SCUser

MJCodingImplementation

@end

MJCodingImplementation 这个宏,帮你实现了 NSCoding 协议,因此不需要再实现 initWithCoder: 和 encodeWithCoder: 了。

第一步已完成,是不是很简单啊~
然后再创建一个数据库管理类:
SCDatabaseTool.h

#import <Foundation/Foundation.h>

@interface SCDatabaseTool : NSObject

/**清除数据库*/
+ (void)clearDatabase;

+ (void)insertUser:(SCUser *)user;
+ (SCUser *)selectUserWithID:(NSString *)userId;
+ (NSArray<SCUser *> *)selectAllUsers;
+ (void)updateUser:(SCUser *)user;
+ (void)deleteUser:(SCUser *)user;

//如果需要别的模型,就接着往下写。每个需要持久化的模型都应该有 增、删、改、查 的基本方法。

@end

SCDatabaseTool.m

#import "SCDatabaseTool.h"
#import "FMDB.h"

@implementation SCDatabaseTool

+ (FMDatabaseQueue *)getCurrentQueue {
    //数据库的文件名很重要,建议以 db_ 开头,后面跟上 userId,这样不同的用户登录,相互数据不影响。
    NSString *fileName = [NSString stringWithFormat:@"db_%@.sqlite", [SCAccountManager defaultManager].account.Id];
    NSString *DBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:fileName];
    FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:DBPath];
    [queue inDatabase:^(FMDatabase *db) {
        [db open];
        //表名的命名规范,以 t_ 开头,后面跟上名字。
        [db executeUpdate:@"create table if not exists t_users (userId text primary key, content blob);"];
        //重点就在这里!我们数据库只有两个字段,一个是主键,也就是 userId,一个是二进制数据内容。
        [db close];
    }];
    return queue;
}

/**清除数据库*/
+ (void)clearDatabase {
    [[self getCurrentQueue] inDatabase:^(FMDatabase *db) {
        [db open];
        [db executeUpdate:@"drop table t_users"];
        [db close];
    }];
}

#pragma mark - 用户相关
+ (void)insertUser:(SCUser *)user {
    [[self getCurrentQueue] inDatabase:^(FMDatabase *db) {
        [db open];
        //插入数据的时候,利用归档,生成二进制数据,写入数据库
        [db executeUpdate:@"insert into t_users(userId, content) values(?, ?)", user.userId, [NSKeyedArchiver archivedDataWithRootObject:user]];
        [db close];
    }];
}

+ (void)updateUser:(SCUser *)user {
    [[self getCurrentQueue] inDatabase:^(FMDatabase *db) {
        [db open];
        //修改的话,直接覆盖之前的二进制数据
        [db executeUpdate:@"update t_users set content = ? where userId = ?", [NSKeyedArchiver archivedDataWithRootObject:user], user.userId];
        [db close];
    }];
}

+ (void)deleteUser:(SCUser *)user {
    [[self getCurrentQueue] inDatabase:^(FMDatabase *db) {
        [db open];
        [db executeUpdate:@"delete from t_users where userId = ?", user.userId];
        [db close];
    }];
}

+ (SCUser *)selectUserWithID:(NSString *)userId {
    __block SCUser *user = nil;
    [[self getCurrentQueue] inDatabase:^(FMDatabase *db) {
        [db open];
        //查询到一条记录,要返回模型,需要反归档。
        FMResultSet *result = [db executeQuery:@"select * from t_users where userId = ?", userId];
        while ([result next]) {
            user = [NSKeyedUnarchiver unarchiveObjectWithData:[result dataForColumn:@"content"]];
            break;
        }
        [db close];
    }];
    return user;
}

+ (NSArray<SCUser *> *)selectAllUsers {
    NSMutableArray *resultArr = [NSMutableArray array];
    [[self getCurrentQueue] inDatabase:^(FMDatabase *db) {
        [db open];
        FMResultSet *result = [db executeQuery:@"select * from t_users"];
        while ([result next]) {
            SCUser *user = [NSKeyedUnarchiver unarchiveObjectWithData:[result dataForColumn:@"content"]];
            [resultArr addObject:user];
        }
        [db close];
    }];
    return [resultArr copy];
}

@end

如此,就完成了,是不是很简单呐~


总结

这么做的好处呢:

  1. 不用担心写错SQL语句,如果一个表中字段过多,还必须一一对应,多一个少一个就会报错。我们只有两个字段,一个主键一个二进制内容。
  2. 数据库的表 和 本地模型 不需要一一对应。如果模型有变动,不需要修改数据库的表。如果按照以前的纯SQLite的方法,比如 user 模型如果多了一个 address 属性,那么还要修改数据库表。很麻烦。但是用现在这个方式,我们什么都不用做~完美兼容!
  3. 数据安全。即使数据库文件被别人从沙盒中提取,也看不到内容。因为我们是利用 OC 的归档生成的二进制。
  4. 内容不限,你甚至可以存储图片、音频、视频,等各种二进制数据。

注意

  • 上面第二条说到,如果添加属性,是没有任何问题的。但是,如果是删除某个属性,就最好先调用 +clearDatabase 把数据库清除了,即 dropTable,其实就是因为这个宏 MJCodingImplementation ,因为反归档的时候,你取出的是有这个字段的,但是却没有这个属性去接收。

猜你喜欢

转载自blog.csdn.net/qinqi376990311/article/details/81538142