iOS___oc 本地持久化详解
概论
所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据。在iOS开发中,有很多数据持久化的方案,接下来我将尝试着介绍一下5种方案:
1、plist文件(属性列表)
2、preference(偏好设置)
3、NSKeyedArchiver (归档)
4、SQLite3
5、CoreData
沙盒
在介绍各种存储方法之前,有必要说明一下沙盒机制。iOS程序默认情况下只能访问程序自己的目录 ,这个目录被称为“沙盒”。
1、结构
既然沙盒就是一个文件夹,那就看看里面有什么吧。沙盒的目录结构如下:
"应用程序包"DocumentsLibrary Caches Preferencestmp NSString *path = [[NSBundle mainBundle] bundlePath]; NSLog(@"%@", path); NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; NSLog(@"%@", path);
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; NSLog(@"%@", path);
|
NSString *path = NSTemporaryDirectory(); NSLog(@"%@", path); NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; NSString *fileName = [path stringByAppendingPathComponent:@"123.plist"]; NSArray *array = @[@"123", @"456", @"789"]; [array writeToFile:fileName atomically:YES];NSArray *result = [NSArray arrayWithContentsOfFile:fileName];NSLog(@"%@", result);
|
2、注意
2.1、偏好设置是专门用来保存应用程序的配置信息的,一般不要在偏好设置中保存其他数据。
2.2、如果没有调用synchronize方法,系统会根据I/O情况不定时刻的保存到文件中。所以如果需要立即写入文件的就必须调用synchronize方法。
2.3、偏好设置会将所有数据保存到同一个文件中。即可preference目录下的一个以此应用包名来命名的plist文件。
NSKeyedArchiver
归档在iOS中是另一种形式的序列化,只要遵循了NSCoding协议的对象都可以通过实现序列化。由于决大多数支持存储数据的Foundation和Cocoa Touch类都遵循了NSCoding协议,因此,对于大多数类来说,归档相对而言还是比较容易实现的。
1、遵循NSCoding协议
NScoding协议声明了两个方法,这两个方法都是必须实现的。一个用来说明如何将对象编码到归档中,另一个说明如何进行接档来获取一个新对象

特别注意:
如果需要归档的类是某个自定义类的子类时,就需要在归档和解档之前先实现父类的归档和解档方法。即 [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];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]; }/*** 打开数据库并创建一个表*/- (void)openDatabase { //1.设置文件名 NSString *filename = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"]; //2.打开数据库文件,如果没有会自动创建一个文件 NSInteger result = sqlite3_open(filename.UTF8String, &_sqlite3); if (result == SQLITE_OK) { NSLog(@"打开数据库成功!"); //3.创建一个数据库表 char *errmsg = NULL; sqlite3_exec(_sqlite3, "CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)", NULL, NULL, &errmsg); if (errmsg) { NSLog(@"错误:%s", errmsg); } else { NSLog(@"创表成功!"); } } else { NSLog(@"打开数据库失败!"); }}执行指令
使用 sqlite3_exec() 方法可以执行任何SQL语句,比如创表、更新、插入和删除操作。但是一般不用它执行查询语句,因为它不会返回查询到的数据。
/*** 往表中插入1000条数据*/- (void)insertData {NSString *nameStr;NSInteger age;for (NSInteger i = 0; i < 1000; i++) { nameStr = [NSString stringWithFormat:@"Bourne-%d", arc4random_uniform(10000)]; age = arc4random_uniform(80) + 20; NSString *sql = [NSString stringWithFormat:@"INSERT INTO t_person (name, age) VALUES('%@', '%ld')", nameStr, age]; char *errmsg = NULL; sqlite3_exec(_sqlite3, sql.UTF8String, NULL, NULL, &errmsg); if (errmsg) { NSLog(@"错误:%s", errmsg); }}NSLog(@"插入完毕!");}
查询指令
前面说过一般不使用 sqlite3_exec()方法查询数据。因为查询数据必须要获得查询结果,所以查询相对比较麻烦。示例代码如下:
-
sqlite3_prepare_v2() : 检查sql的合法性
-
sqlite3_step() : 逐行获取查询结果,不断重复,直到最后一条记录
-
sqlite3_coloum_xxx() : 获取对应类型的内容,iCol对应的就是SQL语句中字段的顺序,从0开始。根据实际查询字段的属性,使用sqlite3_column_xxx取得对应的内容即可。
-
sqlite3_finalize() : 释放stmt
/*** 从表中读取数据到数组中*/- (void)readData { NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1000]; char *sql = "select name, age from t_person;"; sqlite3_stmt *stmt; NSInteger result = sqlite3_prepare_v2(_sqlite3, sql, -1, &stmt, NULL); if (result == SQLITE_OK) { while (sqlite3_step(stmt) == SQLITE_ROW) { char *name = (char *)sqlite3_column_text(stmt, 0); NSInteger age = sqlite3_column_int(stmt, 1); //创建对象 Person *person = [Person personWithName:[NSString stringWithUTF8String:name] Age:age]; [mArray addObject:person]; } self.dataList = mArray; } sqlite3_finalize(stmt);}NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"];FMDatabase *database = [FMDatabase databaseWithPath:path]; if (![database open]) { NSLog(@"数据库打开失败!");}-
具体文件路径,如果不存在会自动创建
-
空字符串@"",会在临时目录创建一个空的数据库,当FMDatabase连接关闭时,数据库文件也被删除
-
nil,会创建一个内存中临时数据库,当FMDatabase连接关闭时,数据库会被销毁
//常用方法有以下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]];- (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"];}[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; }];


浙公网安备 33010602011771号