常规持久化方案
属性列表(Plist)
键值进行存储,不能存储对象。对象需要序列化编码才能写入文件。
`NSAarry`,`NSDictionary`,`NSData`,`NSString`等类型可以直接调用`writeToFile`方法把数据存储到`plist`文件中。
但是数组中的元素或者字典中的元素必须是下面的七种类型:`NSData`,`NSArray`,`NSDictionary`,`NSDate`,`NSString`,`NSNumber`,`Boolean`。
`NSUserDefault`本质上也是存储到`plist`文件中,所以存入`NSUserDefault`当中的对象也应该满足以上七种类型。
适用于应用与少量数据存储,比如登陆的用户信息,应用程序配置信息等。
plist存储的写入:
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
NSString *completePath = [documentPath stringByAppendingPathComponent:@"names"];
[arr writeToFile:completePath atomically:YES];
plist存储的读取:
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
NSString *completePath = [documentPath stringByAppendingPathComponent:@"names"];
NSArray *resultArr = [NSArray arrayWithContentsOfFile:completePath];
NSLog(@"%@",resultArr);
偏好设置存储(NSUserDefaults)
偏好设置一般存储一些像账号密码之类的东西,写入到Preference文件夹.
偏好设置存储写入:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setValue:@"China" forKey:@"china"];
[userDefaults setInteger:1 forKey:@"1"];
[userDefaults setBool:YES forKey:@"yes"];
//立即存储
[userDefaults synchronize];
偏好设置存储读取:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *china = [defaults valueForKey:@"china"];
NSInteger number = [defaults integerForKey:@"1"];
BOOL bu = [defaults boolForKey:@"yes"];
NSLog(@"%@--%d,%ld",china,bu,number);
归档存储
归档存储一般存储自定义对象,并且自定义对象需要遵守NSCoding协议.
首先创建一个自定义对象MCPerson,并且让其遵守NSCoding协议.
#import <Foundation/Foundation.h>
@interface MCPerson : NSObject<NSCoding>
@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *phone;
@end
在.m文件中实现NSCoding协议的两个方法.
#import "MCPerson.h"
@implementation MCPerson
//编码
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeObject:self.phone forKey:@"phone"];
}
//解码
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.phone = [aDecoder decodeObjectForKey:@"phone"];
}
return self;
}
@end
注意事项:如果父类也遵守了NSCoding协议,那么需要在encodeWithCoder:方法中加上[super encodeWithCoder:aCoder].在initWithCoder:方法中将self = [super init]写成self = [super initWithCoder:aDecoder].确保继承自父类的实例变量也能被解码和解码.
归档存储的写入:
MCPerson *person = [[MCPerson alloc]init];
person.name = @"jack";
person.phone = @"12425626236";
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
NSString *completePath = [documentPath stringByAppendingPathComponent:@"person"];
[NSKeyedArchiver archiveRootObject:person toFile:completePath];
归档存储的读取:
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
NSString *completePath = [documentPath stringByAppendingPathComponent:@"person"];
MCPerson *person = [NSKeyedUnarchiver unarchiveObjectWithFile:completePath];
NSLog(@"%@--%@",person.name,person.phone);
对象序列化:
最终也是存为属性列表文件,如果程序中,需要存储对象属性的时候,直接存储对象比较方便,例如有一个设置类,我们可以把设置类的对象直接存储,就没必要再把里面的每一个属性单独存到文件中,对象序列化是将一个实现了NSCoding协议的对象,通过序列化(NSKeyedArchiver)的形式,将对象中的属性抽取出来,转换成二进制流,也就是NSData,NSData可以选择writeToFile或者存储到NSUserdefault中,必须实现两个方法encodeWithCoder,initWithCoder。对象NSData。
NSObject<NSCoding>=====(NSKeyedArchiver)=====>>NSData=====(writeToFile)=====>>File
||
||
||
||
NSUserDefault
sqlite3使用
SQLite简介
SQLite,是一款轻型的数据库也叫做关系型数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。
SQLite3
在XCode工程中,打开targets,在Build Phases下导入Libsqlite.tbd,在需要使用sqlite3的位置导入头文件即可.
生成路径
+(NSString *)path{
NSArray *documentArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath = [documentArr firstObject];
// crylown.db 为数据库的名字
NSString *path = [NSString stringWithFormat:@"%@/crylown.db",documentPath];
return path;
}
创建/打开数据库
sqlite3 *database;
int databaseResult = sqlite3_open([[self path] UTF8String], &database);
if (databaseResult != SQLITE_OK) {
NSLog(@"创建/打开数据库失败,%d",databaseResult);
}
创建表
char *error;
// 建表格式: create table if not exists 表名 (列名 类型,....) 注: 如需生成默认增加的id: id integer primary key autoincrement
const char *createSQL = "create table if not exists list(id integer primary key autoincrement,name char,sex char)";
int tableResult = sqlite3_exec(database, createSQL, NULL, NULL, &error);
if (tableResult != SQLITE_OK) {
NSLog(@"创建表失败:%s",error);
}
添加数据
// 对SQL语句执行预编译
int sqlite3_prepare(sqlite3 *db, const char *sql,int byte,sqlite3_stmt **stmt,const char **tail)
1.db代表打开的数据库连接
2.sql代表的sql语句
3.byte代表SQL语句的最大长度
4.传出参数,指向预编译SQL语句产生的sqlite3_stmt
5.指向SQL语句中未使用的部分
int sqlite3_prapare_v2()版本,代表该函数的最新版本。
// 添加
// sql语句格式: insert into 表名 (列名)values(值)
const char *insertSQL = "insert into haha (name,sex)values('iosRunner','male')";
int insertResult = sqlite3_prepare_v2(database, insertSQL, -1, &stmt, nil);
if (insertResult != SQLITE_OK) {
NSLog(@"添加失败,%d",insertResult);
}
else{
// 执行sql语句
sqlite3_step(stmt);
}
查找数据
//返回sqlite3_stmt(预编译SQL语句产生的结果)
const char* sqlite3_colum_int/text...(sqlite3_stmt *,int N)
根据结果返回的值的类型不同选择int/text等,N代表列名在表中的位置。
// 查找
// sql语句格式: select 列名 from 表名 where 列名 = 参数 注:前面的列名为查询结果里所需要看到的 列名,后面的 列名 = 参数 用于判断删除哪条数据
const char *searchSQL = "select id,name,sex from haha where name = 'puyun2'";
int searchResult = sqlite3_prepare_v2(database, searchSQL, -1, &stmt, nil);
if (searchResult != SQLITE_OK) {
NSLog(@"查询失败,%d",searchResult);
}
else{
while (sqlite3_step(stmt) == SQLITE_ROW) {
// 查询的结果可能不止一条,直到 sqlite3_step(stmt) != SQLITE_ROW,查询结束。
int idWord = sqlite3_column_int(stmt, 0);
char *nameWord = (char *) sqlite3_column_text(stmt, 1);
char *sexWord = (char *)sqlite3_column_text(stmt, 2);
NSLog(@"%d,%s,%s",idWord,nameWord,sexWord);
}
}
修改数据
// 修改
// sql语句格式: update 表名 set 列名 = 新参数 where 列名 = 参数 注:前面的 列名 = 新参数 是修改的值, 后面的 列名 = 参数 用于判断删除哪条数据
const char *changeSQL = "update haha set name = 'buhao' where name = 'iosRunner'";
int updateResult = sqlite3_prepare_v2(database, changeSQL, -1, &stmt, nil);
if (updateResult != SQLITE_OK) {
NSLog(@"修改失败,%d",updateResult);
}
else{
sqlite3_step(stmt);
}
删除数据
// 删除
// sql语句格式: delete from 表名 where 列名 = 参数 注:后面的 列名 = 参数 用于判断删除哪条数据
const char *deleteSQL = "delete from haha where name = 'iosRunner'";
int deleteResult = sqlite3_prepare_v2(database, deleteSQL, -1, &stmt, nil);
if (deleteResult != SQLITE_OK) {
NSLog(@"删除失败,%d",deleteResult);
}
else{
sqlite3_step(stmt);
}
结束处理
// 销毁stmt,回收资源
sqlite3_finalize(stmt);
// 关闭数据库
sqlite3_close(database);
优缺点:
NSOutputStream,NSInputStream
适合大量,重复,有规律的数据存储,而且频繁的读取,删除,过滤数据,这种适合使用数据库。重用的增删改查语句,使用第三方库FMDB更好的处理数据。
CoreData:(以对象方式操作数据库)
CoreData简介
CoreData是一种OR-Mapping的思想,O代表对象Object,R代表relationship,Mapping代表映射,直译过来就是对象关系映射,其实就是把对象的属性和表中的字段自动映射,简化程序员的负担,以面向对象的方式操作数据库。
CoreData本质还是数据库,只不过使用起来更加面向对象,不关注二维的表结构,而是只需要关注对象,纯面向对象的数据操作方式。我们直接使用数据库的时候,如果向数据库中插入数据,一般是把一个对象的属性和数据库中某个表的字段一一对应,然后把对象的属性存储到具体的表字段中,取一条数据的时候,把表中的一行数据取出,同样需要再封装到对象的属性中,这样的方式有点繁琐,不面向对象。CoreData解决的问题就是不需要这个中间的转换过程,看起来是直接把对象存储进去,并且取出,不关心表的存在,实际内部已经帮你做好了映射关系。
Core Date实际上是对SQLite的封装,提供了更高级的持久化方式。在对数据库操作时,不需要使用sql语句,也就意味着即使不懂sql语句,也可以操作数据库中的数据。
CoreData中经常使用的类
NSManagedObjectContext管理对象上下文:相当于FMDB中的FMDatabase对象,我们对数据中的操作先存储到这个上下文中,然后把操作同步到数据库中。
NSManagedObject托管对象:相当于是对表中一行数据的封装。
NSEntityDescription实体描述:相当于在这个对象中定义了数据库中表的结构,比如包含哪些字段等。
NSPersistentStoreCoordinator持久化存储协调器:链接数据库的类,里面包含了数据库的位置,名称等,相当于文件管理器,帮我们创建数据库文件等。
NSMangedObjectModel托管对象模型:里面包含了数据库表,表之间关系的设计模型。其实这个对象里面包含的就是我们使用CoreData时,设计数据库模型Xcdatamodel文件中的信息。
CoreData的详解参见这儿
总结
最后说一下所有的本地持久化数据存储的本质都是写文件,而且只能存到沙盒文件中。沙盒机制是苹果的一项安全机制,本质就是系统给每个应用分配了一个文件夹来存储数据,而且每个应用只能访问分配给自己的那个文件夹,其他应用的文件夹是不能访问的。
沙盒中默认的三个文件夹(支持自己创建新的文件夹):
1、Documents:存储用户相关的数据(用户拍的视频,用户创作的图片,用户唱的歌曲,用户收藏的商品等等)。
2、Library:跟程序相关的数据(程序缓存,程序的配置文件等等)。
3、Temp:放临时文件,不需要永久存储的,比如下载的时候,需要存储到临时文件中,最终copy到Documents或Library中。