一,数据持久化的目的
1,快速展示,提升体验
- 已经加载过的数据,用户下次查看时,不需要再次从网络(磁盘)加载,直接展示给用户
2.节省用户流量
- 对于较大的资源数据进行缓存,下次展示无需下载消耗流量
- 同时降低了服务器的访问次数,节约服务器资源
3,离线使用
- 用户浏览过的数据无需联网,可再次查看
- 部分功能使用解除对网络的依赖。(百度离线地图,图书阅读器)
- 无网络时,允许用户进行操作,等到下次联网时同步到服务端
4,记录用户操作
- 草稿:对于用户需要花费较大成本进行的操作,对用户的每个步骤进行缓存,用户中断操作后,下次用户操作时,直接继续上次的操作
- 已读内容标记缓存,帮助用户识别那些已读
- 搜索记录缓存
二,数据持久化方式分类
1,内存缓存
对于使用频率比较高的数据,从网络或者磁盘加载数据到内存中,使用后并不马上销毁,下次使用时直接从内存加载。
内存是指当前程序的运行空间,缓存速度快容量小,是临时存储文件用的,供CPU直接读取。打开一个程序,他是在内存中存储,关闭程序后内存就又回到原来的空闲空间;
- iOS系统图片加载——[UIImage imageNamed:@“imageName”]
- 网络图片加载三方库:SDWebImage
2,磁盘缓存
将从网络加载的,用户操作产生的数据写入到磁盘中,用户下次查看,继续操作时,直接从磁盘加载使用。
磁盘是程序的存储空间,缓存容量大、速度慢、可持久化。与内存不同的是磁盘是永久存储东西的,只要里面存放东西,不管运行不运行 ,他都占用空间。
- 用户输入内存草稿缓存(评论,文本编辑)
- 网络图片加载第三方库:SDWebImage
- 搜索历史缓存
三,缓存策略
硬件的存储设施是有限的,仅能存储有限的数据,但我们需要更高的命中率。就需要缓存算法。
- FIFO(First in First out)
核心思想:一个数据最先进入缓存中,则应该最早淘汰掉。类似实现一个按照时间先后顺序的队列来管理缓存,将淘汰最早访问的数据缓存。
- LFU(LeastFrequentlyUsed)
记录用户对数据的访问次数,将访问次数多的数据降序排列在一个容器中,淘汰访问次数最少的数据。
- LRU(LeastRecentlyUsed)
LRU ,该算法维护一个缓存项队列,队列中的缓存项按每项的最后被访问时间排序。当缓存空间已满时,将处于队尾,即删除最后一次被访问时间距现在最久的项,将新的区段放入队列首部。
- LRU_K (leastRecentlyUsed)
具体来说它多维护一个队列,记录所有缓存数据被访问的历史。仅当数据的访问次数达到K次的时候,才将数据放入缓存。当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据。
- 2Q (Two queues)
2Q算法类似于LRU-2,不同点在于2Q将LRU-2算法中的访问历史队列(注意这不是缓存数据的)改为一个FIFO缓存队列,即:2Q算法有两个缓存队列,一个是FIFO队列,一个是LRU队列。
- MQ(Multiu Queue)
MQ算法根据优先级(访问频率)将数据划分为多个LRU队列,其核心思想是:优先缓存访问次数多的数据
四,数据持久化方案
1,内存缓存
实现内存缓存的技术手段包括苹果官方提供的NSURLCache
,NSCache
,还有性能和API上比较有优势的开源缓存库YYCache
、PINCache
等。
NSCache
NSCache
是苹果提供的一套缓存机制,用法和NSMutableDictionary
类似,在AFNetworking
,SDWebImage
,Kingfisher
中都有用到。- 当内存不足时
NSCache
会自动释放内存。NSCache
设置缓存对象数量和占用的内存大小,当缓存超出了设置会自动释放内存。 NSCache
是Key-Value
数据结构,通过key来获取缓存对象。NSCache
中的数据在APP重启后会消失,因为NSCache
只是将数据保存在内存的NSCache
和NSMutableDictionary
的区别:NSCache
是线程安全的,不需要加线程锁,而NSMutableDictionary
线程不安全。
2,磁盘缓存
NSUserDefault
适合小规模数据,弱业务相关数据的缓存。
keychain
Keychain是苹果提供的带有可逆加密的存储机制,普遍用在各种存用户名、密码的需求上。另外,Keychain是系统级存储,还可以被iCloud同步,即使App被删除,Keychain数据依然保留,用户下次安装App,可以直接读取,通常会用来存储用户唯一标识串。所以需要加密、同步iCloud的敏感小数据,一般使用Keychain存取。
文件存储
- Plist:一般结构化的数据可以Plist的方式去持久化
- archive:Archive方式可以存取遵循协议的数据,比较方便的是存取使用的都是对象,不过中间的序列化和反序列化需要花费一定的性能,可以在想要使用对象直接进行磁盘存取时使用。
- Stream:指文件存储,一般用来存图片、视频文件等数据
数据库存储
数据库适合存取一些关系型的数据;可以在有大量的条件查询排序类需求时使用。
- Core Data:苹果官方封装的ORM(Object Relational Mapping)
- FMDB:github最受欢迎的iOS sqlite 封装开源库之一
- WCDB:微信团队在自己使用的sqlite封装基础上的开源实现,具有ORM(Object Relational Mapping)的特性,支持iOS、Android。
- Realm:由Y Combinator孵化的创业团队开源出来的一款跨平台(iOS、Android)移动数据库。
FMDB
FMDB是iOS平台的SQLite的数据库框架,以OC的方式封装了SQLite的C语言API。
优点:
- 使用起来更加面向对象,省去了很多麻烦冗余的C语言代码
- 对比苹果自带的Core Data框架,更加轻量级和灵活。
- 提供了多线程安全的数据库操作方法,有效地防止数据混乱。
FMDB有三个主要的类
FMDatabase
:一个FMDatabase对象就代表一个单独的SQLite数据库
用来执行SQL语句。
FMResultSet
:使用FMDatabase执行查询后的结果集。
FMDatabaseQueue
:用于在多线程中执行多个查询或更新,它是线程安全的。
// 建表
NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS t_test_1 ('%@' INTEGER PRIMARY KEY AUTOINCREMENT,'%@' TEXT NOT NULL, '%@' TEXT NOT NULL, '%@' TEXT NOT NULL, '%@' TEXT NOT NULL, '%@' TEXT NOT NULL, '%@' TEXT NOT NULL, '%@' TEXT NOT NULL, '%@' TEXT NOT NULL, '%@' INTEGER NOT NULL, '%@' FLOAT NOT NULL)", KEY_ID, KEY_MODEL_ID, KEY_MODEL_NAME, KEY_SERIES_ID, KEY_SERIES_NAME, KEY_TITLE, KEY_PRICE, KEY_DEALER_PRICE, KEY_SALES_STATUS, KEY_IS_SELECTED, KEY_DATE];
FMDatabaseQueue *_dbQueue = [FMDatabaseQueue databaseQueueWithPath:@"path"];
[_dbQueue inDatabase:^(FMDatabase *db) {
BOOL result = [db executeUpdate:sql];
if (result) {
//
}
}];
// 插入一条数据
NSString *insertSql = [NSString stringWithFormat:@"INSERT INTO 't_test_1'(%@,%@,%@,%@,%@,%@,%@,%@,%@,%@) VALUES(\"%@\",\"%@\",\"%@\",\"%@\",\"%@\",\"%@\",\"%@\",\"%@\",%d,%.2f)", KEY_MODEL_ID, KEY_MODEL_NAME, KEY_SERIES_ID, KEY_SERIES_NAME, KEY_TITLE, KEY_PRICE, KEY_DEALER_PRICE, KEY_SALES_STATUS, KEY_IS_SELECTED, KEY_DATE, model.model_id, model.model_name, model.Id, model.Name, model.title, model.price, model.dealer_price, model.sales_status, isSelected,time];
[_dbQueue inDatabase:^(FMDatabase *db) {
BOOL result = [db executeUpdate:sql];
if (result) {
//
}
}];
// 更新
NSString *sql = @"UPDATE t_userData SET userName = ? , userAge = ? WHERE id = ?";
[_dbQueue inDatabase:^(FMDatabase *db) {
BOOL res = [db executeUpdate:sql,_nameTxteField.text,_ageTxteField.text,_userId];
if (result) {
//
}
}];
// 删除
NSString *str = [NSString stringWithFormat:@"DELETE FROM t_userData WHERE id = %ld",userid];
[_dbQueue inDatabase:^(FMDatabase *db) {
BOOL res = [db executeUpdate:str];
if (res) {
//
}
}];
// 查找
[_dbQueue inDatabase:^(FMDatabase *db) {
FMResultSet *resultSet = [db executeQuery:@"SELECT * FROM message"];
NSMutableArray<Message *> *messages = [[NSMutableArray alloc] init];
while ([resultSet next]) {
Message *message = [[Message alloc] init];
message.localID = [resultSet intForColumnIndex:0];
message.content = [resultSet stringForColumnIndex:1];
message.createTime = [NSDate dateWithTimeIntervalSince1970:[resultSet doubleForColumnIndex:2]];
message.modifiedTime = [NSDate dateWithTimeIntervalSince1970:[resultSet doubleForColumnIndex:3]];
[messages addObject:message];
}
}];
沙盒储存
沙盒:iOS中每个应用程序都有一个独立的文件夹,这个文件夹就是沙盒。沙盒用来存储app的本地文件,例如:音频、视频、图片文件等。并且每个app的沙盒都是独立的,即当前app没有权限访问其他app的沙盒,所以说沙盒存储较之更安全。
沙盒的层次结构
获取沙盒根目录
NSString *directory = NSHomeDirectory();
NSLog(@"directory:%@", directory);
打印结果
/Users/fuchuang/Library/Developer/CoreSimulator/Devices/021352E7-E025-419B-9045-0BA88DD4BE35/data/Containers/Data/Application/3A470BFA-2A06-44E3-879C-34B13DB37A39
获取路径
Documents
:保存应用运行的时生成的需要持久化的数据,会被自动备份在iCloud中。
一般我们在项目中,我们会把一些用户的登录信息以及搜索历史记录等一些关键数据存储到这里。Library
:默认存放设置和其他状态信息,除了caches子目录之外其他目录都会被iclude同步。Application Support
:此目录包含应用程序用来运行但应对用户隐藏的文件,如游戏的新关卡等文件。Caches
:目录用于存储应用程序的缓存数据,比如下载的文件、图像缓存、网络请求缓存等。这些数据可以在应用程序的后续运行中快速访问,以提高性能和用户体验。iOS 系统可能会在设备存储空间紧张时清理 Caches 目录中的文件。Caches 目录中的文件不会被备份到 iCloud,因此适合存储可以重新生成或重新下载的数据Cooikes
:系统会自动将App中网络请求的cookie保存为文件。Preferences
:保存应用的所有偏好设置。UserDefaults 生成的 plist 文件就会保存该目录下。SplashBoard
:存储启动屏缓存,缓存文件格式为 ktx,本质上就是图片,如果启动屏不生效的问题可以考虑从删除该路径下相关缓存文件这个角度解决。
SystemData
:存放系统数据,无对外暴露的接口。tmp
:临时文件夹(系统会不定期删除里面的文件)。