iOS的数据存储机制
沙盒机制
应用沙盒文件夹包含了:
-
Application(应用程序包):包含了所有的资源文件和和可执行文件,上架前经过数字签名,上架后不可修改。
-
Documents:文档目录,要保存程序生成的数据,会自动被分到iCloud中。保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录。(注意点:不要保存从网络上下载的文件,否则会无法上架!)
-
Library:用户偏好,使用 NSUserDefault 直接读写!如果要想数据及时写入磁盘,还需要调用一个同步方法。该目录下有两个子目录:
- Caches:存放体积大又不需要备份的数据
- Preference:保存应用的所有偏好设置,iCloud会备份设置信息
- Tmp:临时文件,系统会自动清理。重新启动就会清理。保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录。重新启动手机,tmp 目录会被清空。系统磁盘空间不足时,系统也会自动清理
获取沙盒目录
- 沙盒根目录:
NSString *home = NSHomeDirectory();
- 利用 NSSearchPathForDirectoriesInDomains 函数获取Documents目录:
// NSUserDomainMask 代表从用户文件夹下找
// YES 代表展开路径中的波浪字符“~”
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO);
// 在iOS中,只有一个目录跟传入的参数匹配,所以这个集合里面只有一个元素
NSString *documents = [array objectAtIndex:0];
- 获取tmp目录:
NSString *tmp = NSTemporaryDirectory();
- Library/Caches:利用
NSSearchPathForDirectoriesInDomains
函数 (将函数的第2个参数改为:NSCachesDirectory
即可) - Library/Preference:通过NSUserDefaults类存取该目录下的设置信息。
iOS的数据持久化方式
内存缓存和磁盘缓存
缓存分为内存缓存和磁盘缓存两种。
其中内存是指当前程序的运行空间,缓存速度快容量小,是临时存储文件用的,供CPU直接读取,比如说打开一个程序,他是在内存中存储,关闭程序后内存就又回到原来的空闲空间;
磁盘是程序的存储空间,缓存容量大、速度慢、可持久化。与内存不同的是磁盘是永久存储东西的,只要里面存放东西,不管运行不运行 ,他都占用空间!磁盘缓存是存在Library/Caches。
NSCache
plist(属性列表)
Plist(NSArray\NSDictionary),只存储数组、字典,但是数组和字典里不能有自定义对象。
属性列表是一种XML格式的文件,拓展名为plist如果对是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,就可以使用writeToFile:atomically:方法直接将对象写到属性列表文件中。
将NSArray、NSDictionary等归档到plist文件中,可以有效地实现数据持久化,方便在应用的不同会话中保存和加载数据。
可以将用户的偏好设置、应用配置项等保存到plist文件中,用户在重新启动应用时能够恢复之前的设置。
将应用所需的静态数据(例如,菜单项、静态内容、预定义选项等)保存到plist文件中,方便在应用中读取和使用。
对于不需要数据库管理的小规模数据,可以通过plist文件来实现简单的数据持久化,避免使用更加复杂的数据库系统。
将一个NSDictionary对象归档到一个plist属性列表中:
// 将数据封装成字典
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:@"张三" forKey:@"name"];
[dict setObject:@"155xxxxxxx" forKey:@"phone"];
[dict setObject:@"27" forKey:@"age"];
// 将字典持久化到Documents/stu.plist文件中
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO);
NSString *documentsDirectory = [array firstObject];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"stu.plist"];
[dict writeToFile:path atomically:YES];
恢复NSDictionary读取属性列表,恢复NSDictionary对象:
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO);
NSString *documentsDirectory = [array firstObject];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"stu.plist"];
// 读取Documents/stu.plist的内容,实例化
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(@"name:%@", [dict objectForKey:@"name"]);
NSLog(@"phone:%@", [dict objectForKey:@"phone"]);
NSLog(@"age:%@", [dict objectForKey:@"age"]);
偏好设置Preferences
偏好设置指的是使用NSUserDefaults类来存储和检索应用程序的设置和偏好数据。NSUserDefaults提供了一种简单的方法来存储和读取用户的偏好数据,例如应用的设置、用户选项等。它适用于存储简单的数据,如字符串、数字、布尔值、日期和数据对象。
如,保存用户名、字体大小、是否自动登录:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"张三" forKey:@"username"];
[defaults setFloat:18.0f forKey:@"text_size"];
[defaults setBool:YES forKey:@"auto_login"];
读取上次保存的设置
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *username = [defaults stringForKey:@"username"];
float textSize = [defaults floatForKey:@"text_size"];
BOOL autoLogin = [defaults boolForKey:@"auto_login"];
UserDefaults设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。所以调用了set方法之后数据有可能还没有写入磁盘应用程序就终止了。出现以上问题,可以通过调用synchornize方法[defaults synchornize];强制写入。
归档和解档
如果仅仅存储简单的数据类型(如字符串、数字),可以直接写入文件。但是当需要存储复杂的对象(如自定义类、嵌套的数组和字典)时,归档和解档可以将这些复杂对象转换为可存储的格式,并在需要时恢复原始对象。
-
简化数据存储和读取:
归档和解档过程自动处理对象的属性和关系,简化了数据存储和读取的代码。开发者无需手动处理对象的每个属性,也无需手动解析存储的数据。 -
确保数据完整性和一致性:
通过归档和解档,可以确保对象的所有属性都被正确存储和恢复,避免手动存储和读取过程中可能出现的遗漏和错误。 -
支持持久化和传输:
序列化的数据可以方便地写入文件、数据库或通过网络传输。这样,应用程序可以在不同设备或会话之间共享数据。 -
安全性:
使用安全编码选项,归档和解档可以防止反序列化过程中出现的数据篡改和不安全数据的注入。
NSKeyedArchiver
NSKeyedArchiver如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,可以直接用NSKeyedArchiver进行归档和恢复。
不是所有的对象都可以直接用这种方法进行归档,只有遵守了NSCoding协议的对象才可以。
NSCoding 协议有2个方法:
encodeWithCoder
: 每次归档对象时,都会调用这个方法。一般在这个方法里面指定如何归档对象中的每个实例变量,可以使用encodeObject:forKey:
方法归档实例变量。initWithCoder
: 每次从文件中恢复(解码)对象时,都会调用这个方法。一般在这个方法里面指定如何解码文件中的数据为对象的实例变量,可以使用decodeObject:forKey
方法解码实例变量。
NSData
将多个对象写入到同一个文件中,那么就要使用NSData来进行归档对象。 NSData可以为一些数据提供临时存储空间,以便随后写入文件,或者存放从磁盘读取的文件内容。可以使用[NSMutableData data]
创建可变数据空间。

归档(编码)
// 新建一块可变数据区
NSMutableData *data = [NSMutableData data];
// 将数据区连接到一个NSKeyedArchiver对象
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
// 开始存档对象,存档的数据都会存储到NSMutableData中
[archiver encodeObject:person1 forKey:@"person1"];
[archiver encodeObject:person2 forKey:@"person2"];
// 存档完毕(一定要调用这个方法)
[archiver finishEncoding];
// 将存档的数据写入文件
[data writeToFile:path atomically:YES];
NSData-从同一文件中恢复2个Person对象恢复(解码):
// 从文件中读取数据
NSData *data = [NSData dataWithContentsOfFile:path];
// 根据数据,解析成一个NSKeyedUnarchiver对象
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
Person *person1 = [unarchiver decodeObjectForKey:@"person1"];
Person *person2 = [unarchiver decodeObjectForKey:@"person2"];
// 恢复完毕
[unarchiver finishDecoding];
利用归档实现深复制:
// 临时存储person1的数据
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person1];
// 解析data,生成一个新的Person对象
Student *person2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// 分别打印内存地址
NSLog(@"person1:0x%x", person1); // person1:0x7177a60
NSLog(@"person2:0x%x", person2); // person2:0x7177cf0
SQL本地数据库
CoreData
CoreData 是基于 sqlite 的封装。
FMDB
FMDB是iOS平台的SQLite数据库框架。
FMDB以OC的方式封装了SQLite的C语言API
使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码。
对比苹果自带的Core Data框架,更加轻量级和灵活。
提供了多线程安全的数据库操作方法,有效地防止数据混乱。