iOS 数据的持久化存储

所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据。在iOS开发中,有很多数据持久化的方案。(iOS程序默认情况下只能访问程序自己的目录,这个目录被称为“沙盒”。)

plist文件(属性列表)

preference(偏好设置)

NSKeyedArchiver(归档)

SQLite 3

CoreData

Documents: 最常用的目录,iTunes同步该应用时会同步此文件夹中的内容,适合存储重要数据。NSString *path = [[NSBundle mainBundle] bundlePath];

NSLog(@"%@", path);

Library/Caches: iTunes不会同步此文件夹,适合存储体积大,不需要备份的非重要数据。

NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;

NSLog(@"%@", path);

Library/Preferences: iTunes同步该应用时会同步此文件夹中的内容,通常保存应用的设置信息。

NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;

NSLog(@"%@", path);

tmp: iTunes不会同步此文件夹,系统可能在应用没运行时就删除该目录下的文件,所以此目录适合保存应用中的一些临时文件,用完就删除。

NSString *path = NSTemporaryDirectory();

NSLog(@"%@", path);

二。plist文件

plist文件是将某些特定的类,通过XML文件的方式保存在目录中。

可以被序列化的类型只有如下几种:

NSArray;

NSMutableArray;

NSDictionary;

NSMutableDictionary;

NSData;

NSMutableData;

NSString;

NSMutableString;

NSNumber;

NSDate;

1.获得文件路径

NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;

NSString *fileName = [path stringByAppendingPathComponent:@"123.plist"];

2.存储

NSArray *array = @[@"123", @"456", @"789"];

[array writeToFile:fileName atomically:YES];

3.读取

NSArray *result = [NSArray arrayWithContentsOfFile:fileName];

NSLog(@"%@", result);

4.注意

只有以上列出的类型才能使用plist文件存储。

存储时使用writeToFile: atomically:方法。 其中atomically表示是否需要先写入一个辅助文件,再把辅助文件拷贝到目标文件地址。这是更安全的写入文件方法,一般都写YES。

读取时使用arrayWithContentsOfFile:方法。

NSKeyedArchiver

归档在iOS中是另一种形式的序列化,只要遵循了NSCoding协议的对象都可以通过它实现序列化。由于决大多数支持存储数据的Foundation和Cocoa Touch类都遵循了NSCoding协议,因此,对于大多数类来说,归档相对而言还是比较容易实现的。

1.遵循NSCoding协议

NSCoding协议声明了两个方法,这两个方法都是必须实现的。一个用来说明如何将对象编码到归档中,另一个说明如何进行解档来获取一个新对象。

遵循协议和设置属性

//1.遵循NSCoding协议

@interface Person : NSObject  //2.设置属性

@property (strong, nonatomic) UIImage *avatar;

@property (copy, nonatomic) NSString *name;

@property (assign, nonatomic) NSInteger age;

@end

实现协议方法

//解档

- (id)initWithCoder:(NSCoder *)aDecoder {

if ([super init]) {

self.avatar = [aDecoder decodeObjectForKey:@"avatar"];

self.name = [aDecoder decodeObjectForKey:@"name"];

self.age = [aDecoder decodeIntegerForKey:@"age"];

}

return self;

}

//归档

- (void)encodeWithCoder:(NSCoder *)aCoder {

[aCoder encodeObject:self.avatar forKey:@"avatar"];

[aCoder encodeObject:self.name forKey:@"name"];

[aCoder encodeInteger:self.age forKey:@"age"];

}

特别注意

如果需要归档的类是某个自定义类的子类时,就需要在归档和解档之前先实现父类的归档和解档方法。即 [super encodeWithCoder:aCoder] 和 [super initWithCoder:aDecoder] 方法;

2.使用

需要把对象归档是调用NSKeyedArchiver的工厂方法 archiveRootObject: toFile: 方法。

 

NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];

Person *person = [[Person alloc] init];

person.avatar = self.avatarView.image;

person.name = self.nameField.text;

person.age = [self.ageField.text integerValue];

[NSKeyedArchiver archiveRootObject:person toFile:file];

需要从文件中解档对象就调用NSKeyedUnarchiver的一个工厂方法 unarchiveObjectWithFile: 即可。

NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];

Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:file];

if (person) {

self.avatarView.image = person.avatar;

self.nameField.text = person.name;

self.ageField.text = [NSString stringWithFormat:@"%ld", person.age];

}

3.注意

必须遵循并实现NSCoding协议

保存文件的扩展名可以任意指定

继承时必须先调用父类的归档解档方法

FMDB

1.简介

FMDB是iOS平台的SQLite数据库框架,它是以OC的方式封装了SQLite的C语言API,它相对于cocoa自带的C语言框架有如下的优点:

使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码

对比苹果自带的Core Data框架,更加轻量级和灵活

提供了多线程安全的数据库操作方法,有效地防止数据混乱

注:FMDB的gitHub地址

2.核心类

FMDB有三个主要的类:

FMDatabase

一个FMDatabase对象就代表一个单独的SQLite数据库,用来执行SQL语句

FMResultSet

使用FMDatabase执行查询后的结果集

FMDatabaseQueue

用于在多线程中执行多个查询或更新,它是线程安全的

3.打开数据库

和c语言框架一样,FMDB通过指定SQLite数据库文件路径来创建FMDatabase对象,但FMDB更加容易理解,使用起来更容易,使用之前一样需要导入sqlite3.dylib。打开数据库方法如下:

NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"];

FMDatabase *database = [FMDatabase databaseWithPath:path];

if (![database open]) {

NSLog(@"数据库打开失败!");

}

值得注意的是,Path的值可以传入以下三种情况:

具体文件路径,如果不存在会自动创建

空字符串@"",会在临时目录创建一个空的数据库,当FMDatabase连接关闭时,数据库文件也被删除

nil,会创建一个内存中临时数据库,当FMDatabase连接关闭时,数据库会被销毁

4.更新

在FMDB中,除查询以外的所有操作,都称为“更新”, 如:create、drop、insert、update、delete等操作,使用executeUpdate:方法执行更新

//常用方法有以下3种:

- (BOOL)executeUpdate:(NSString*)sql, ...

- (BOOL)executeUpdateWithFormat:(NSString*)format, ...

- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments

//示例

[database executeUpdate:@"CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)"];

//或者

[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES(?, ?)", @"Bourne", [NSNumber numberWithInt:42]];

5.查询

查询方法也有3种,使用起来相当简单:

1

2

3

- (FMResultSet *)executeQuery:(NSString*)sql, ...

- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...

- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments

查询示例:

//1.执行查询

FMResultSet *result = [database executeQuery:@"SELECT * FROM t_person"];

//2.遍历结果集

while ([result next]) {

NSString *name = [result stringForColumn:@"name"];

int age = [result intForColumn:@"age"];

}

6.线程安全

在多个线程中同时使用一个FMDatabase实例是不明智的。不要让多个线程分享同一个FMDatabase实例,它无法在多个线程中同时使用。 如果在多个线程中同时使用一个FMDatabase实例,会造成数据混乱等问题。所以,请使用 FMDatabaseQueue,它是线程安全的。以下是使用方法:

创建队列。

1

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];

使用队列

[queue inDatabase:^(FMDatabase *database) {

[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_1", [NSNumber numberWithInt:1]];

[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_2", [NSNumber numberWithInt:2]];

[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_3", [NSNumber numberWithInt:3]];

FMResultSet *result = [database executeQuery:@"select * from t_person"];

while([result next]) {

}

}];

而且可以轻松地把简单任务包装到事务里:

[queue inTransaction:^(FMDatabase *database, BOOL *rollback) {

[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_1", [NSNumber numberWithInt:1]];

[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_2", [NSNumber numberWithInt:2]];

[database executeUpdate:@"INSERT INTO t_person(name, age) VALUES (?, ?)", @"Bourne_3", [NSNumber numberWithInt:3]];

FMResultSet *result = [database executeQuery:@"select * from t_person"];

while([result next]) {

}

//回滚

*rollback = YES;

}];

FMDatabaseQueue 后台会建立系列化的G-C-D队列,并执行你传给G-C-D队列的块。这意味着 你从多线程同时调用调用方法,GDC也会按它接收的块的顺序来执行。

posted on 2016-08-16 13:20  SK-Devin  阅读(162)  评论(0编辑  收藏  举报

导航