iOS开发中的本地数据存储(持久化)

来源:互联网 发布:上古卷轴5初始捏脸数据 编辑:程序博客网 时间:2024/04/30 20:30

问题:如何把一个包含自定义对象的数组序列化到磁盘?

涉及的知识点:iOS开发中的本地数据存储(持久化)
一、iOS开发中本地存储主要有三种形式
· plist文件(属性列表)
· preference(偏好设置)
· NSKeyedArchiver(归档)
· SQLite 3
· CoreData

1、plist文件
plist文件是将某些特定的类通过xml文件的方式保存在目录中。
(1)可以被序列化的类型NSArrasy(NSMutableArray)、NSDictionary(NSMutableDictionary)、NSDate(NSMutableDate)、NSString(NSMutableString)、NSNumber、NSDate;
(2)使用方法
a.获得文件路径 NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *fileName = [path stringByAppendingPathComponent:@ “123.plist” ];
b.存储
NSArray *array = @[@ “123” , @ “456” , @ “789” ];
[array writeToFile:fileName atomically:YES];
c.读取
NSArray *result = [NSArray arrayWithContentsOfFile:fileName];
NSLog(@ “%@” , result);

(3)部分参数介绍
a.总体来说:NSSearchPathForDirectionInDomains作用
找Documents文件夹方式
    part1 搜索的文件名字
    part2 搜索文件的范围
    part3 是否返回绝对路径 YES 是返回绝对路径 NO 返回相对路径
    通过搜素方式找到沙盒下 Document文件夹路径

NSDocumentDirectory:Document目录
NSCachesDirectory:Cache目录
NSLibraryDirectory:Library目录
NSDocumentionDirectory对应于程序中的Library/Documentation路径,这个路径是没有读写权限的,所以看不到文件生成。

NSUserDomainMask = 1, // 用户主目录,放在用户的私人项中 NSLocalDomainMask = 2, //放在这台设备上,这台机器上的每个人都可用
NSNetworkDomainMask = 4,// 放在当地的网络区域,在联网时任何人可用
NSSystemDomainMask = 8, // 苹果提供,不可修改
NSAllDomainsMask = 0x0ffff // 所有区域,包括以上所有项及未来的项

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

2 、preference(偏好设置)
(1)介绍:NSUserDefaults适合存储轻量级的本地数据,用来保存应用程序设置和属性、用户保存的数据,比如要保存一个登陆界面的数据,用户再次打开程序或开机后这些数据仍然存在。NSUserDefaults可以存储的数据类型包括:NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary。如果要存储其他类型,则需要转换为前面的类型,才能用 NSUserDefaults 存储。
(2)使用

//存储数据
-(void)saveToUserDefaults:(NSString*)tosaveedString withKey:(NSString *)tosaveedKey
{
NSUserDefaults * tmp = [NSUserDefaults standardUserDefaults];
if (tmp) {
[tmp setObject:tosaveedString forKey:tosaveedKey];
[tmp synchronize];
}
}

//读出数据
-(NSString )restoreFromUserDefaults:(NSString )key
{
NSString * rtn = nil;
NSUserDefaults * tmp = [NSUserDefaults standardUserDefaults];
if (tmp) {
rtn = [tmp objectForKey:key];
}
return rtn;
}

//存入数据
[self saveToUserDefaults:textPass.text withKey:@”saveUserPass”];
//使用读出数据
NSString *isLoginCode=[self restoreFromUserDefaults:@”saveUserPass”];

//1.获得NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中写入内容
[userDefaults setObject:@ “AAA”  forKey:@ “a” ];
[userDefaults setBool:YES forKey:@ “sex” ];
[userDefaults setInteger:21 forKey:@ “age” ];
//2.1立即同步
[userDefaults synchronize];
//3.读取文件
NSString *name = [userDefaults objectForKey:@ “a” ];
BOOL sex = [userDefaults boolForKey:@ “sex” ];
NSInteger age = [userDefaults integerForKey:@ “age” ];
NSLog(@ “%@, %d, %ld” , name, sex, age);
2.注意
· 偏好设置是专门用来保存应用程序的配置信息的,一般不要在偏好设置中保存其他数据。
· 如果没有调用synchronize方法,系统会根据I/O情况不定时刻地保存到文件中。所以如果需要立即写入文件的就必须调用synchronize方法。
· 偏好设置会将所有数据保存到同一个文件中。即preference目录下的一个以此应用包名来命名的plist文件。

3、NSKeyedArchiver(归档)
(1)介绍
NSKeyedArchiver:采用归档的形式来保存数据,该数据对象需要遵守NSCoding协议,并且该对象对应的类必须提供encodeWithCoder:和initWithCoder:方法。前一个方法告诉系统怎么对对象进行编码,而后一个方法则是告诉系统怎么对对象进行解码。
(2)使用
自定义对象序列化到磁盘
Possession:

@interface Possession:NSObject{//遵守NSCoding协议

NSString *name;//待归档类型

}
@implementation Possession

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

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

}

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

name=[[aDeCoder decodeObjectforKey:@”name”] retain];

}
归档操作:
如果对Possession对象allPossession归档保存,只需要NSCoder子类NSKeyedArchiver的方法archiveRootObject:toFile: 即可。
NSString *path = [self possessionArchivePath];
[NSKeyedArchiver archiveRootObject:allPossessions toFile: path ]
解压操作:
同样调用NSCoder子类NSKeyedArchiver的方法unarchiveRootObject:toFile: 即可
allPossessions = [[NSKeyedUnarchiver unarchiveObjectWithFile:path] retain];
缺点:归档的形式来保存数据,只能一次性归档保存以及一次性解压。所以只能针对小量数据,而且对数据操作比较笨拙,即如果想改动数据的某一小部分,还是需要解压整个数据或者归档整个数据。
4、SQLite 3
之前的所有存储方法,都是覆盖存储。如果想要增加一条数据就必须把整个文件读出来,然后修改数据后再把整个内容覆盖写入文件。所以它们都不适合存储大量的内容。
1.字段类型
表面上SQLite将数据分为以下几种类型:
· integer : 整数
· real : 实数(浮点数)
· text : 文本字符串
· blob : 二进制数据,比如文件,图片之类的
实际上SQLite是无类型的。即不管你在创表时指定的字段类型是什么,存储是依然可以存储任意类型的数据。而且在创表时也可以不指定字段类型。SQLite之所以什么类型就是为了良好的编程规范和方便开发人员交流,所以平时在使用时最好设置正确的字段类型!主键必须设置成integer
2. 准备工作
准备工作就是导入依赖库啦,在iOS中要使用SQLite3,需要添加库文件:libsqlite3.dylib并导入主头文件,这是一个C语言的库,所以直接使用SQLite3还是比较麻烦的。
3.使用
· 创建数据库并打开
操作数据库之前必须先指定数据库文件和要操作的表,所以使用SQLite3,首先要打开数据库文件,然后指定或创建一张表。

/**
*  打开数据库并创建一个表
*/
- (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);
}

0 0
原创粉丝点击