iOS数据持久化方案
在iOS开发中,有很多数据持久化的方案,接下来说一下常用的5种方案:
plist文件(属性列表)
preference(偏好设置)
NSKeyedArchiver(归档)
SQLite
CoreData
1.plist文件(属性列表)
plist文件是将某些特定的类,通过XML文件的方式保存在目录中。
- (void)plist
{
// Document路径
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *path = [docPath stringByAppendingPathComponent:@"demo.plist"];
// 1.写入文本
NSString *str = @"Jerry is a good man.";
BOOL writeSuccess = [str writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
if (writeSuccess) {
NSLog(@"写入成功");
}
// 读取文本
NSString *readStr = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSLog(@"%@", readStr);
// 2.写入数组
NSArray *array = @[@"Jerry", @"Lily"];
writeSuccess = [array writeToFile:path atomically:YES];
if (writeSuccess) {
NSLog(@"写入成功");
}
// 读取数组
NSArray *readArray = [NSArray arrayWithContentsOfFile:path];
NSLog(@"%@", readArray);
// 3.写入字典
NSDictionary *dict = @{ @"name" : @"Jerry", @"age" : @10 };
writeSuccess = [dict writeToFile:path atomically:YES];
if (writeSuccess) {
NSLog(@"写入成功");
}
// 读取字典
NSDictionary *readDict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(@"%@", readDict);
// 4.写入NSData
NSData *data = [@"this is data" dataUsingEncoding:NSUTF8StringEncoding];
writeSuccess = [data writeToFile:path atomically:YES];
if (writeSuccess) {
NSLog(@"写入成功");
}
// 读取NSData
NSData *readDate = [NSData dataWithContentsOfFile:path];
NSString *redaDataStr = [[NSString alloc] initWithData:readDate encoding:NSUTF8StringEncoding];
NSLog(@"%@", redaDataStr);
}
2.preference(偏好设置)
偏好设置是专门用来保存应用程序的配置信息的,一般不要在偏好设置中保存其他数据。
如果没有调用synchronize方法,系统会根据I/O情况不定时刻地保存到文件中。所以如果需要立即写入文件的就必须调用synchronize方法。
偏好设置会将所有数据保存到同一个文件中。即preference目录下(沙盒根路径/Library/Preferences)的一个以此应用包名来命名的plist文件。
- (void)preference
{
// 1.获得NSUserDefaults单例
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
// 2.写入内容
[userDefaults setObject:@"Jerry" forKey:@"name"];
[userDefaults setBool:YES forKey:@"sex"];
[userDefaults setInteger:10 forKey:@"age"];
// 3.立即同步
[userDefaults synchronize];
// 4.读取内容
NSString *name = [userDefaults objectForKey:@"name"];
BOOL sex = [userDefaults boolForKey:@"sex"];
NSInteger age = [userDefaults integerForKey:@"age"];
NSLog(@"%@, %d, %ld", name, sex, age);
}
3.NSKeyedArchiver(归档)
归档在iOS中是另一种形式的序列化,只要遵循了NSCoding协议的对象都可以通过它实现序列化。由于决大多数支持存储数据的Foundation和Cocoa Touch类都遵循了NSCoding协议,因此,对于大多数类来说,归档相对而言还是比较容易实现的。
注意点:
必须遵循并实现NSCoding协议
保存文件的扩展名可以任意指定
继承时必须先调用父类的归档解档方法
@interface Person : NSObject <NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
#import "Person.h"
@implementation Person
// 归档
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInt:self.age forKey:@"age"];
}
// 解档
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntForKey:@"age"];
}
return self;
}
@end
// 使用
- (void)archiver
{
// Document路径
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *path = [docPath stringByAppendingPathComponent:@"archiver.plist"];
Person *p = [[Person alloc] init];
p.name = @"Jerry";
p.age = 10;
// 对象归档
BOOL archiverSuccess = [NSKeyedArchiver archiveRootObject:p toFile:path];
if (archiverSuccess) {
NSLog(@"对象归档成功");
}
// 对象解档
Person *unArchiverPerson = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSLog(@"name = %@, age = %d", unArchiverPerson.name, unArchiverPerson.age);
}
4.SQLite
以上方法不太适合存储大量的内容,如果数据量较大一般会使用第三方封装的数据库库, 比如FMDB.
// DBManager.h
#import "FMDB.h"
static NSString * const TABLE_BASE_DATA = @"T_BASE_DATA"; // 基础数据表
@class FMDatabase;
@interface DBManager : NSObject
/** 数据库管理类单例 */
+ (instancetype)sharedManager;
@property (nonatomic, strong) FMDatabase *db; // 数据库
@end
// DBManager.m
static DBManager *_manager;
static NSString * const DATABASE_NAME = @"qinglian.sqlite"; // 数据库表
@implementation DBManager
#pragma mark - 单例
+ (instancetype)sharedManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_manager = [[DBManager alloc] init];
[_manager createDatabase];
[_manager createTables];
});
return _manager;
}
+ (instancetype)alloc
{
if (_manager) {
NSException *exception = [NSException exceptionWithName:@"DBManager init NSException." reason:@"请使用DBManager的单例方法." userInfo:nil];
[exception raise];
}
return [super alloc];
}
/** 创建数据库 */
- (void)createDatabase
{
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
// ILog(@"documentPath = %@", documentPath);
NSString *dbPath = [documentPath stringByAppendingPathComponent:DATABASE_NAME];
self.db = [FMDatabase databaseWithPath:dbPath];
}
/** 创建所有的表 */
- (void)createTables
{
if (self.db && [self.db open]) {
[self createBaseDataTable];
} else {
ILog(@"数据库打开失败");
}
}
/** 创建基础数据表 */
- (void)createBaseDataTable
{
NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@ (ID INTEGER PRIMARY KEY AUTOINCREMENT,FID TEXT,CHECKPOINT_CODE TEXT,INSTALLATION_CODE TEXT,INSTALLATION_TYPE TEXT,INSTALLATION_TYPE_NAME TEXT,INSTALLATION_NAME TEXT,INSTALLATION_PILE_KNO TEXT,CULVERT_STYLE TEXT,SEGMENT TEXT,SEGMENT_NAME TEXT,MAINTAIN_COMPANY TEXT,MAINTAIN_COMPANY_NAME TEXT,LONGITUDE DOUBLE,LATITUDE DOUBLE,CORRECT_LONGITUDE DOUBLE,CORRECT_LATITUDE DOUBLE)", TABLE_BASE_DATA];
BOOL result = [_db executeUpdate:sql];
if (!result) {
ILog(@"创建表:TABLE_BASE_DATA失败");
}
}
// 使用方法
/** 保存基础数据到数据库 */
+ (void)insertBaseDataToDB:(NSArray *)baseDatas
{
FMDatabase *db = [DBManager sharedManager].db;
if (![db open]) {
return;
}
NSString *insertSql = [NSString stringWithFormat:@"INSERT INTO %@ (FID,CHECKPOINT_CODE,INSTALLATION_CODE,INSTALLATION_TYPE,INSTALLATION_TYPE_NAME,INSTALLATION_NAME,INSTALLATION_PILE_KNO,CULVERT_STYLE,SEGMENT,SEGMENT_NAME,MAINTAIN_COMPANY,MAINTAIN_COMPANY_NAME,LONGITUDE,LATITUDE,CORRECT_LONGITUDE,CORRECT_LATITUDE) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", TABLE_BASE_DATA];
NSInteger count = baseDatas.count ? : 0;
for (int index = 0; index < count; index++) {
BaseDataModel *baseDataModel = baseDatas[index];
BOOL result = [db executeUpdate:insertSql, ...];
if (!result) {
ILog(@"基础数据插入数据库失败");
}
}
[db close];
}
5.CoreData
CoreData使用得比较少,后面补上.