iOS应用数据存储的常用方式
XML属性列表(plist)归档
Preference(偏好设置)
NSKeyedArchiver归档 / NSKeyedUnarchiver解档
SQLite3
FMDB
Core Data
1、沙盒
沙盒也叫沙箱,英文standbox,其原理是通过重定向技术,把程序生成和修改的文件定向到自身文件夹中。在沙盒机制下,每个程序之间的文件夹不能互相访问。iOS系统为了保证系统安全,采用了这种机制
iOS 应用程序在安装时,会创建属于自己的沙盒文件,应用程序不能直接访问其他应用程序的沙盒文件,当应用程序需要向外部请求或接收数据时,都需要经过权限认证,否则,无法获取到数据。
应用程序中所有的非代码文件都保存在沙盒中,比如图片、声音、属性列表,sqlite数据库和文本文件等。
沙盒的的根目录有三个文件夹,分别是 Documents,Library,tmp
- AppName.app 目录:这是应用程序的程序包目录,包含了所有的资源文件和可执行文件。由于应用程序必须经过签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动
- Documents:一般保存应用程序本身产生文件数据,例如游戏进度,绘图软件的绘图等, iTunes备份和恢复的时候,会包括此目录
- tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录
- Library/Caches: 存放缓存文件,iTunes不会备份,因此文件不会因APP退出而删除(一般使用SDWebImage的缓存资源都是保存到这来)
- Library/Preference:保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录
如何获取到对应目录下的路径?
// home
NSString *home = NSHomeDirectory();
// Documents
NSString *docDir1 = [NSString stringWithFormat:@"%@/%@",home,@"Documents"];
NSString *docDir2 = [home stringByAppendingPathComponent:@"Documents"];
NSString *docDir3 = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// Library/Caches
NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
// Library/Preferences
NSString *preferencesDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"Preferences"];
// tmp
NSString *tempDir = NSTemporaryDirectory();
2、plist
plist是一种明文的轻量级存储方式,这种方式的安全性几乎为0.
- 特点:只能存储OC常用数据类型(NSString、NSDictionary、NSArray、NSData、NSNumber等类型)而不能直接存储自定义模型对象
如果想存储自定义模型对象 -> 只能将自定义模型对象转换为字典存储;
/**
* Plist文件
*/
- (void)setUpPlist {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [path stringByAppendingPathComponent:@"Person.plist"];
NSDictionary *dict = @{
@"name" : @"William",
@"age" : @18,
@"height" : @1.75f
};
// 将数据写入Plist
[dict writeToFile:filePath atomically:YES];
NSLog(@"%@", filePath);
// 读取plist中的数据
NSDictionary *dicts = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSLog(@"dict = %@",dicts);
NSString *str = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSLog(@"str = %@", str);
}
3、Preference(偏好设置)
偏好设置的使用非常方便快捷,我们一般使用它来进行一些设置的记录,比如用户名,开关是否打开等设置。
Preference是通过NSUserDefaults来使用的,是通过键值对的方式记录设置
- 特点:存储数据不需要关心文件名称,快速存储键值对
重点说明:
(1)偏好设置是专门用来保存应用程序的配置信息的, 一般情况不要在偏好设置中保存其他数据。如果利用系统的偏好设置来存储数据, 默认就是存储在Preferences文件夹下面的,偏好设置会将所有的数据都保存到同一个文件中。
(2)使用偏好设置对数据进行保存之后, 它保存到系统的时间是不确定的,会在将来某一时间点自动将数据保存到Preferences文件夹下面,如果需要即刻将数据存储,可以使用[defaults synchronize];
(3)注意点:所有的信息都写在一个文件中,对比简单的plist可以保存和读取基本的数据类型。
(4)步骤:获取NSuserDefaults,保存(读取)数据
(5)NSUserDefaults可以存储的数据类型包括:NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary。如果要存储其他类型,则需要转换为前面的类型,才能用NSUserDefaults存储。
/**
* 偏好设置
*/
// 不能保存自定义的对象
- (void)setUpSaveUserDefaults {
// 获取NSUsersDefault对象(是个单利)
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSLog(@"%@", defaults);
// 保存数据(注意: 如果设置的数据没有同步, 那么会在将来的某个时刻自动将数据保存到Preference文件夹下面)
[defaults setObject:@"Mr.William" forKey:@"name"];
[defaults setInteger:23 forKey:@"age"];
[defaults setDouble:1.75f forKey:@"height"];
[defaults setObject:@"man" forKey:@"gender"];
// 强制让数据立刻保存
[defaults synchronize];
}
// 读取数据
- (void)setUpReadUserDefaults {
// 同样获取NSUserDefault对像
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// 读取数据
NSString *name = [defaults objectForKey:@"name"];
NSString *gender = [defaults objectForKey:@"gender"];
NSInteger age = [defaults integerForKey:@"age"];
double height = [defaults doubleForKey:@"height"];
NSLog(@"name=%@,gender=%@,age=%ld,height=%.1f",name,gender,age,height);
}
4、NSKeyedArchiver归档 / NSKeyedUnarchiver解档
归档和解档会在写入、读出数据之前进行序列化、反序列化,数据的安全性相对高一些。
- 特点:实现对自定义类的对象进行归档.
序列化与反序列化:
序列化: 将一个OC对象转换成NSData(二进制)的操作就叫做对象的序列化
反序列化: 将本地的二进制数据转为一个OC对象的操作就叫做反序列化
OC对象需要通过遵守NSCoding协议,并且实现协议中的两个方法,才能支持序列化和反序列化操作.
#import <Foundation/Foundation.h>
@class Person;
@interface ArchiverManager : NSObject
+ (instancetype)sharedInstance;
/**
运用NSKeyedArchiver归档数据
**/
- (BOOL)writePersonData:(Person *)person;
/**
使用NSKeyedUnarchiver解档数据
**/
- (Person *)readPersonData;
/**
清除数据
**/
- (BOOL)cleanData;
@end
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
#import "ArchiverManager.h"
@implementation ArchiverManager
+ (instancetype)sharedInstance
{
static ArchiverManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [self new];
});
return sharedInstance;
}
/**
运用NSKeyedArchiver归档数据
**/
- (BOOL)writePersonData:(Person *)person
{
NSString *path = [self fullPath];
NSFileManager *manager = [NSFileManager defaultManager];
BOOL remove = [manager removeItemAtPath:path error:nil];
if (remove)
{
NSLog(@"删除成功");
}
return [NSKeyedArchiver archiveRootObject:person toFile:path];
}
/**
使用NSKeyedUnarchiver解档数据
**/
- (Person *)readPersonData
{
NSString *path = [self fullPath];
NSFileManager *fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath:path])
{
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
return person;
}
return nil;
}
/**
清除数据
**/
- (BOOL)cleanData
{
NSString *path = [self fullPath];
NSFileManager *fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath:path])
{
return [fm removeItemAtPath:path error:nil];
}
return NO;
}
- (NSString *)fullPath
{
NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
NSString *path=[docPath stringByAppendingPathComponent:@"Person.data"];
return path;
}
@end
@implementation Person
//归档
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeInteger:self.age forKey:@"age"];
}
//解档
-(instancetype)initWithCoder:(NSCoder *)decoder
{
if (self=[super init]) {
self.name = [decoder decodeObjectForKey:@"name"];
self.age = [decoder decodeIntegerForKey:@"age"];
}
return self;
}
具体使用
/**
* 归档
*/
- (void)archiverData
{
//创建Person对象
Person *person = [Person new];
person.name = @"relax";
person.age = 18;
[[ArchiverManager sharedInstance] writePersonData:person];
}
/**
* 解档
*/
- (void)unarchiverData
{
Person *person = [[ArchiverManager sharedInstance] readPersonData];
NSLog(@"name=%@,age=%ld,",person.name,person.age);
}
5、SQLite3
SQLite 不区分大小写,但也有需要注意的地方,例如GLOB 和 glob 具有不同作用。
- 特点:5种基本数据类型 text、integer、float、boolean、blob
SQLite3是无类型的,在创建的时候你可以不声明字段的类型,不过还是建议加上数据类型