IOS数据存储
一、应用沙盒
每个iOS应用都有自己的应用沙盒(应用沙盒就是应用的文件夹),与其他文件系统隔离。应用必须待在自己的沙盒里,其他应用不能访问该沙盒。
2.应用沙盒结构应用程序包: 包含了所有的 资源文件 和 可执行文件
Documents: 保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录。保存相对重要的数据
tmp: 保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备
份该目录。保存不重要的并且大的数据。
Library/Caches: 保存应用运行时生成的需要持久化的数据,iTunes同步设备时不会备份该目录。一般存储体积大、不需要备份的非重要数据
Library/ Preference: 保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录。该目录由系统
管理,无需我们来管理。通常用来存储一些基本的软件配置信息, 比如记住密码、自动登录等。
3. 获取应用沙盒目录
#import "NSString+Path.h"
@implementation NSString (Path)
- (NSString *)cachePath{
// 获取文件名
NSString *fileName = [self lastPathComponent];
// 获取文件夹路径
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
return [path stringByAppendingPathComponent:fileName];
}
- (NSString *)documentPath{
NSString *fileName = [self lastPathComponent];
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
return [path stringByAppendingPathComponent:fileName];
}
- (NSString *)tmpPath{
NSString *fileName = [self lastPathComponent];
NSString *path = NSTemporaryDirectory();
return [path stringByAppendingPathComponent:fileName];
}
@end
二、iOS数据存储的常用方式
1、XML属性列表(plist)归档
2、Preference(偏好设置) 本质还是通过“plist”来存储数据, 但是使用更简单(无需关注文件、文件夹路径和名称)
3、NSKeyedArchiver归档(NSCoding) 把任何对象,直接保存为文件
4、SQLite 当非常大量的数据存储时使用
5、Core Data 对SQLite的封装
1>属性列表
属性列表: 属性列表是一种XML格式的文件,拓展名为plist
如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,就可以使用writeToFile:atomically:方法直接将对象写到属性列表文件中
// 将数据封装成字典
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:@"母鸡" forKey:@"name"];
[dict setObject:@"15013141314" forKey:@"phone"];
[dict setObject:@"27" forKey:@"age"];
// 将字典持久化到Documents/stu.plist文件中
[dict writeToFile:path atomically:YES];
恢复 NSDictionary
// 读取Documents/stu.plist的内容,实例化NSDictionary
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(@"name:%@", [dict objectForKey:@"name"]);
NSLog(@"phone:%@", [dict objectForKey:@"phone"]);
NSLog(@"age:%@", [dict objectForKey:@"age"]);
2>偏好设置
每个应用都有个NSUserDefaults实例,通过它来存取偏好设置
[defaults setObject:@"itcast" forKey:@"username"];
[defaults setFloat:18.0f forKey:@"text_size"];
[defaults setBool:YES forKey:@"auto_login"];
注意:UserDefaults设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。所以调用了set方法之后数据有可能还没有写入磁盘应用程序就终止了。出现以上问题,可以通过调用synchornize方法强制写入 [defaults synchornize];
3>NSKeyedArchiver
如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,可以直接用NSKeyedArchiver进行归档和恢复
NSCoding协议
encodeWithCoder:每次归档对象时,都会调用这个方法。一般在这个方法里面指定如何归档对象中的每个实例变量,可以使用encodeObject:forKey:方法
归档实例变量
initWithCoder:每次从文件中恢复(解码)对象时,都会调用这个方法。一般在这个方法里面指定如何解码文件中的数据为对象的实例变量,可以使用
decodeObject:forKey方法解码实例变量
归档 NSArray
编码:[NSKeyedArchiver archiveRootObject:arraytoFile:path]
解码: [NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
归档 Person 对象
Person:
@interface Person : NSObject<NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) float height;
@end
@implementation Person
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeInt:self.age forKey:@"age"];
[encoder encodeFloat:self.height forKey:@"height"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self.name = [decoder decodeObjectForKey:@"name"];
self.age = [decoder decodeIntForKey:@"age"];
self.height = [decoder decodeFloatForKey:@"height"];
return self;
}
- (void)dealloc {
[super dealloc];
[_name release];
}
@end
归档
Person *person = [[[Person alloc] init] autorelease];
person.name = @"name";
person.age = 30;
person.height = 1.80f;
[NSKeyedArchiver archiveRootObject:person toFile:path];
解码
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
注意:
如果父类也遵守了NSCoding协议,请注意:应该在encodeWithCoder:方法中加上一句[super encodeWithCode:encode],确保继承的实例变量也能被编码,即也能被归档;还应该在initWithCoder:方法中加上一句self = [super initWithCoder:decoder],确保继承的实例变量也能被解码,即也能被恢复
4>SQLite
1⃣️SQLite介绍
1. 什么是SQLite
SQLite是一款轻型的嵌入式数据库
SQLite占用资源非常低,在嵌入式设备中只需要几百K的内存
但处理速度比MySql、PostgreSQL这两款著名的数据库都还快
2. 什么是数据库
数据库(DataBase)是按照数据结构来组织、存储和管理数据的仓库
目前数据库可以分为两大类:
关系型数据库(主流数据库)
对象型数据库
数据库的存储是以表为单位的
3. 数据库存数据的步骤
新建一个数据库
新建一张表(table)
添加多个字段(column,列,属性)
添加多行记录(row,每行存放读个字段对应的值)
4. SQLite相对于大型的数据库有什么特点?
>sqlite它是一个小型数据库"关系型"管理系统
>处理速度非常快
>sqlite是一个跨平台,linux/unix/window上都是可以用
"SQLServer" -微软开发, 它只在window上运行
>SQLite里其实是没有真实的数据类型,定义数据类型是可有可无,在Mysql,Oracle,SQLServer的数据类型是有用
>删除字段和修改字段的属性在SQLite里是不支持的,在MySQL/Oracle里支持
2⃣️SQLite语句
"1.删除表"
>drop table if exists t_user;
"2.创建表"
主键primary key: 唯一标识一条记录,不能有重复的,不允许为空
UNIQUE 此字段的数据,唯一,不能重复
>create table t_user (
userId integer primary key,
username varchar(25) UNIQUE,
pwd varchar(8),
birthday timestamp);
"3.给表添加额外字段,如性别"
>alter table t_user add sex varchar(6)
删除字段和修改字段的属性在SQLite里是不支持的,在MySQL/Oracle里支持
"4.插入数据"
insert into t_user values(1,’zhangsan’,’1234','2015-09-09');
insert into t_user (username,pwd,birthday) values(‘lisi’,’12345’,datetime());
insert into t_user (username,pwd,birthday) values(‘wangwu’,’123456’,datetime());
“5.查询数据"
•普通查询
select * from t_user;
select username,pwd from t_user;
•条件查询
select * from t_user where username='zhangsan';
select pwd from t_user where username='zhangsan';
•模糊查询
select * from t_user where username like 'zhan%';//以zhan开头
select * from t_user where username like '%zhan';//以zhan结尾
select * from t_user where username like '%zhan%';//包涵zhan
•多条件
select * from t_user where username like '%zhan%' and sex='male';
•排序(生日)
SELECT username,strftime('%Y-%m-%d',birthday) FROM t_user order by birthday asc;
SELECT username,strftime('%Y-%m-%d',birthday) FROM t_user order by birthday desc;
•排序(先性别,再生日)
SELECT username,strftime('%Y%m%d %H:%M:%S',birthday),sex FROM t_user order by sex,birthday asc;//先性别升序,生日升序
SELECT username,strftime('%Y%m%d %H:%M:%S',birthday),sex FROM t_user order by sex desc,birthday asc;//先性别降序,生日升序
•分组
SELECT sex FROM t_user GROUP BY sex;
•分页
limit a,b //a索引,b长度
总共 6数据 ,想分3页显示 每一页 2
1页 0,2
2页 2,2
3页 4,2
SELECT * from t_user LIMIT 4,2;
“6.更新数据"
update t_user SET sex = 'male' where username = 'david';
“7.函数查询"
•记录数
select count(*) From t_user;
select count(*) From t_user where sex='male';
•最大数、最小数
select max(userID) From t_user;
select min(userID) From t_user;
•平均年龄总和
alter table t_user add age integer;
update t_user set age = 20 where 1=1;
select avg(age) as '平均年龄',sum(age) as '年龄总合' FROM t_user;
“8.删除"
•delete from t_user where username = 'zhangsan';
“9.外键关联"
drop TABLE if exists t_payment;
//交易表
create table t_payment (
paymentId integer primary key,
orderDesc text,
tradeNo varchar(36),
tradeDate timestamp,
amount float,
paymentType integer,
paymentStatus integer,
userId integer,
foreign key (userId) references t_user(userId));
//多表查询
•所有定单并显示定单所属的用户名
select u.username,p.orderDesc,p.paymentType from t_user u,t_payment p where u.userId = p.userId and p.paymentType = 0;
•左关联 '关键字会从左表 (table_name1) 那里返回所有的行,即使在右表 (table_name2) 中没有匹配的行。'
select u.username,p.orderDesc,p.paymentType from t_user u left join t_payment p on u.userId = p.userId;
•右关联在sqlite中,是不支持的
3⃣️FMDB
FMDB有三个主要的类:
①FMDatabase
一个FMDatabase对象就代表一个单独的SQLite数据库
用来执行SQL语句
②FMResultSet
使用FMDatabase执行查询后的结果集
③FMDatabaseQueue
用于在多线程中执行多个查询或更新,它是线程安全的
打开数据库:通过指定SQLite数据库文件路径来创建FMDatabase对象
FMDatabase *db = [FMDatabase databaseWithPath:path];
if (![db open]) {
NSLog(@"数据库打开失败!");
}
文件路径有三种情况
具体文件路径:如果不存在会自动创建
空字符串@"":会在临时目录创建一个空的数据库,当FMDatabase连接关闭时,数据库文件也被删除
nil:会创建一个内存中临时数据库,当FMDatabase连接关闭时,数据库会被销毁
更新:
- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
[db executeUpdate:@"UPDATE t_student SET age = ? WHERE name = ?;", @20, @"Jack"]
查询:
- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
// 查询数据
FMResultSet *rs = [db executeQuery:@"SELECT * FROM t_student"];
// 遍历结果集
while ([rs next]) {
NSString *name = [rs stringForColumn:@"name"];
int age = [rs intForColumn:@"age"];
double score = [rs doubleForColumn:@"score"];
}
FMDatabase这个类是线程不安全的,如果在多个线程中同时使用一个FMDatabase实例,会造成数据混乱等问题
为了保证线程安全,FMDB提供方便快捷的FMDatabaseQueue类
FMDatabaseQueue的创建
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:path];
简单使用
[queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];
[db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];
[db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"];
FMResultSet *rs = [db executeQuery:@"select * from t_student"];
while ([rs next]) {
// …
}
}];
使用事务
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];
[db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];
[db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"];
FMResultSet *rs = [db executeQuery:@"select * from t_student"];
while ([rs next]) {
// …
}
}];
事务回滚
*rollback = YES;
5>CoreData
1.创建模型文件
2.添加实体
3.创建实体类
4.生成上下文 关联模型文件生成数据库
5.保存对象到数据库
6.从数据库获取对象
7.更新数据
8.删除数据