代码改变世界

iOS开发系列-常见离线存储方式

2018-08-20 15:29  iCoderHong  阅读(546)  评论(0编辑  收藏  举报

概述

在很多社交App手机在手机没有网络时,重新启动应用,依然能否展示上次访问的数据,提高用户体验,这个就是离线数据存储的运用场景。在iOS开发中常见的离线存储技术有Plist存储、个人偏好存储、解归档、CoreData、SQLite。

Plist存储

Plist存储就是将数据写入一个指定沙盒路径的Plist文件中。如果想要更新已经写入Plist文件中的数据需要全部重新写入,因此不适合大量数据存储。并且Plist存储写入的指定类型中的数据Array、 Dictionary、 String、 Boolean、 Date 、Data 、Number

示例:

- (void)plistCache
{
    // Plist存储
    // 指定写入Plist文件路径 "/Library/Caches"
    NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
    NSLog(@"%@", cachePath);
    
    // 字符串写入plist存储
    NSString *str = @"123";
    [str writeToFile:[cachePath stringByAppendingPathComponent:@"test.plist"] atomically:YES encoding:NSUTF8StringEncoding error:nil];
    
    // 数组写入plist存储
    [@[@"jake", @"roes"] writeToFile:[cachePath stringByAppendingPathComponent:@"array.plist"] atomically:YES];
    
    // 字典写入plist存储
    NSDictionary *dict = @{@"name":@"jake", @"age":@13, @"height": @1.98};
    [dict writeToFile:[cachePath stringByAppendingPathComponent:@"dict.plist"] atomically:YES];
    
    // NSData写入plist存储
    NSData *imgData = UIImagePNGRepresentation([UIImage imageNamed:@"timg.jpeg"]);
    [imgData writeToFile:[cachePath stringByAppendingPathComponent:@"data.jpeg"] atomically:YES];
    
}

执行代码,查看沙盒,数据被写入指定的Plist文件中。

偏好设置

偏好设置本质就是Plist存储,系统提供了一个单例NSUserDefaults,每次可以将单个类型value写入沙盒的/Library/Preference目录下的Plist文件中。该Plist命名是应用的Bundle Id。

由于个人偏好设置本质也是Plist存储,因此可以写入数据的基本数据类型只能是Array、 Dictionary、 String、 Boolean、 Date 、Data 、Number

- (void)NSUserDefaultsCache
{
    // NSUserDefaults(个人偏好存储)
    [[NSUserDefaults standardUserDefaults] setObject:@"jake" forKey:@"name"];
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"sex"];
    [[NSUserDefaults standardUserDefaults] setDouble:1.78 forKey:@"height"];
    // 立即同步
    [[NSUserDefaults standardUserDefaults] synchronize];
    
    // 移除个人偏好存储plist中的key-value
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"name"];
    
    // 反归档从沙盒获取数据
    [NSKeyedUnarchiver unarchiveObjectWithFile:@"name"];
}

在实际的开发中我们可能存储我们自定对象中的数据,这种需求上面的Plist存储和个人偏好设置存储就不能满足需求了,需要用到下面介绍的解归档存储

解归档

解归档就是将遵守NSCoding对象的数据写入指定沙盒路径的二进制文件。如果利用解归档技术将内存中自定义对象中数据写入沙盒指定文件需要先遵守NSCoding协议。

NSCoding协议

这里我定义Person类,实现NSCoding协议,并将Person实例中的数据通过反归档写入沙盒

/***********************************Person.h*********************************************/
#import <Foundation/Foundation.h>

@interface Person : NSObject<NSCoding>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) int  age;
@property (nonatomic, assign) double height;
@property (nonatomic, assign) BOOL sex;
@end

/***********************************Person.m*********************************************/
#import "Person.h"

@implementation Person
- (void)encodeWithCoder:(NSCoder *)aCoder
{
    // 告诉系统哪些属性需要归档
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeDouble:self.height forKey:@"height"];
    [aCoder encodeInt:self.age forKey:@"age"];
    [aCoder encodeBool:self.sex forKey:@"sex"];
}

- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.height = [aDecoder decodeDoubleForKey:@"height"];
        self.age = [aDecoder decodeIntForKey:@"age"];
        self.sex = [aDecoder decodeBoolForKey:@"sex"];
    }
    return self;
}

- (NSString *)description
{
    NSString *string = [NSString stringWithFormat: @"name=%@, height=%f, age=%d, sex=%d", self.name, self.height, self.age, self.sex];
    return string;
}

解归档Person示例
- (void)archiverCache
{
    // 获取路径
    NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
    
    // 归档
    // 对于开发中自定义的数据模型的储存,我们可以考虑使用归档储存方案。
    Person *p = [[Person alloc] init];
    p.name = @"jake";
    p.height = 1.78;
    p.age = 18;
    p.sex = YES;
    [NSKeyedArchiver archiveRootObject:p toFile:[cachePath stringByAppendingPathComponent:@"pData"]];
    
    // 返归档从沙盒中取出Person示例对象
    id person = [NSKeyedUnarchiver unarchiveObjectWithFile:[cachePath stringByAppendingPathComponent:@"pData"]];
    NSLog(@"%@", person);
}

CoreData

CoreData 是一个纯粹面向对象的框架,可以管理实体以及实体之间的关联关系的持久化,也就是我们常说的数据持久化。

但是由于它是一个重量级的数据库管理,产生很多代码量。对于复杂的联合表查询不适用 并且出错不容易解决或找到问题,需要有很深的开发功底。

目前开发一般使用嵌入式数据库SQLite替代。这里就不详细介绍了。

SQLite

SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。
SQLite 是非常小的,是轻量级的,完全配置时小于 400KiB,省略可选功能配置时小于250KiB。
SQLite 可在 UNIX(Linux, Mac OS-X, Android, iOS)和 Windows(Win32, WinCE, WinRT)中运

SQLite数据类型

SQLite数据类型 对应OC类型
INTEGER 整型
TEXT 文本
REAL 浮点型
BLOB 二进制数据
注意:SQLite这里没有对应OC中的BOOL类型。

SQLite语句

对于SQLite的语句已基本与MySQLite一直,可以参考我的博客MySQL
这里有两点需要注意下,设置主键自动增长使用AUTOINCREMENT,不要使用MySQL的auto_increment带下划线的样式。

SQLite在应用

对于iOS程序使用SQLite苹果提供了静态库

sqlite3提供的接口纯C语言,开发起来比较费劲,一般我们使用GitHub的对其封装的第三方库FMDB
对于FMDB的使用可以参考我简书上的博客FMDB使用