iOS学习之路04 - 数据存储
-
文件管理
-
iOS沙盒机制
-
iOS应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒,所以所有的非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文本文件等。
- 每个应用程序都有自己的存储空间
- 应用程序不能翻过自己的围墙去访问别的存储空间的内容
- 应用程序请求的数据都要通过权限检测,假如不符合条件的话,不会被放行
-
通过这张图只能从表层上理解sandbox是一种安全体系,应用程序的所有操作都要通过这个体系来执行,其中核心内容是:sandbox对应用程序执行各种操作的权限限制。
-
-
目录结构
应用程序可以访问手机任何文件、任何路径,但各应用程序每安装一次,他的沙盒路径都不样,并且文件名很长还被加密了,因此其他程序无法得知哪个沙盒目录是哪个程序的
- Documents:苹果建议将程序中建立的或在程序中浏览到的文件数据保存在该目录下;(iTunes备份和恢复的时候会包括此目录)
- Library:存储程序的默认设置或其它状态信息;(iTunes备份和恢复的时候会包括此目录)
- Library/Caches:存放缓存文件;(iTunes不会备份此目录,此目录下文件不会在应用退出删除)
- tmp:提供一个即时创建临时文件的地方。(系统重启后会删除tmp里的所有文件)
-
文件操作
- NSHomeDirectory() C语言函数,返回沙盒目录的全路径
-
NSSearchPathForDirectoryInDomains(…, …, …)
C语言函数,返回沙盒目录下对应目录的路径
参数一:需要的目录名称(枚举类型)
参数二:从哪个主目录下获取NSUserDomainMask就是当前沙盒目录
参数三:是否返回完全的路径,NO则返回类似与~/相对路径
-
NSFileManager
OC对象,文件管理类,创建、删除文件夹/文件
-
属性列表(Property List) plist文件
-
读取的方式
-
plist文件中,最外层是字典时
NSDictionary *dic = [NSDictionary dictionaryWithContentOfFile: path];
-
-
写入的方式
-
NSUserDefaults类操作plist文件
创建方式
NSUserDefaults *user = [NSUserDefaults standardUserDefaults];
添加键值对
[user setObject: @"wo" forKey: @"wo"];
[user setInteger: 100 forKey: @"number"];
同步数据
[user synchronize];
移除指定key对应的值
[user removeObjectForKey: @"update"];
- NSArray、NSMutableArray、NSDictionary、NSMutableDictionary、NSData、NSMutableData、NSString、NSMutableString、NSDate这些对象可以通过实例方法 writeToFile: (NSString *)path atomically: (BOOL)userAuxiliaryFile;向plist文件写入数据,这些数据被写入后,会覆盖原先的数据
-
-
-
数据归档
-
基本概念
- 数据归档是将数据以二进制的形式保存,达到数据的隐蔽性
- 要实现归档必须实现<NSCoding>协议,对数据进行编码和解码操作
- 常用的NSFoundation类都实现了<NSCoding>协议,所以可以直接归档
-
自定的对象归档需要自己实现<NSCoding>协议
- (void) encodeWithCoder: (NSCoder *)aCoder 编码
- (instancetype) initWithCoder: (NSCoder *)aDecoder 解码
方法对对象的属性编码和解码
-
基本用法
-
第一种方式
-
[NSKeyedArchiver archiveRootObject: "要归档的对象" toFile: "保存到的文件路径"]
数据归档会将数据以二进制的形式保存
-
利用NSKeyedUnarchiver类的方法unarchiveObjectWithFile: "文件路径"
NSMutableDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithFile: "文件路径"];
-
-
第二种方式
-
将对象编码为NSData(二进制类型)保存,再将数据写入指定的文件中
NSData *userData = [NSKeyedArchiver archiveDataWithRootObject: user];
[userData writeToFile: "保存到的文件路径" atomically: YES];
-
将编码的NSData类型数据反编码为原来的对象类型
User *user = [NSKeyedUnarchiver unarchiveObjectWithData: userData];
-
-
-
-
-
SQLite数据库(sqlite3)
- 首先学会如何导入<sqlite3.h>库、libsqlite3.dylib库
-
创建数据库
-
使用sqlite3_open函数打开数据库
eg:int result = sqlite3_open([filePath UTF8String], &_sqlDB);
sqlite3_open():当数据库不存在时,创建数据库,并打开数据库。如果存在,则只打开数据库
filePath:数据库文件(data.sqlite)的全路径
_sqlDB:数据库结构体,sqlite类型,sqlite *_sqlDB;
result:31种情况,常有:0(SQLITE_OK,操作成功)、100(SQLITE_ROW,还有下一行)
-
使用sqlite3_exec函数执行创建表的SQL语句,创建数据库表
eg:int result = sqlite3_exec(_sqlDB, [sql UTF8String], NULL, NULL, &error);
sqlite3_exec():执行SQL语句,返回值为31种状态中的一种
_sqlDB:同上
sql:创建表的SQL语句,NSString类型
error:错误信息,NSError类型
result:同上
-
使用sqlite3_close函数释放资源
eg:int result = sqlite3_close(_sqlDB);
sqlite3_close():关闭数据库,释放资源
reslut:同上
_sqlDB:同上
-
-
查询数据
- 使用sqlite3_open函数打开数据库
-
使用sqlite3_prepare_v2函数预处理SQL语句
eg:int result = sqlite3_prepare_v2(_sqlDB, [sql UTF8String], -1 &_stmt, NULL);
sqlite3_prepare_v2():将SQL编译成二进制代码,提高SQL语句的执行速度
_stmt:SQL语句处理结构体
-1:代表全部sql字符串长度
-
使用sqlite3_bind_text函数绑定参数
eg:int result = sqlite3_bind_text(_stmt, 1, [userName UTF8String], -1, NULL);
sqlite3_bind_text():绑定SQL语句参数。在SQL语句中带有问号,这个问号就是要绑定的参数,问号是占位符。
NSString *qsql = @"SELECT * FROM User where userName = ?";
sqlite3_bind_*()是SQL语句参数绑定系列函数
_stmt:同上
1:从第几个"?"(参数)开始
userName:NSString类型,?所代表的值
-1:同上
-
使用sqlite3_step函数执行SQL语句,遍历结果集
eg:int result = sqlite3_step(_stmt)
sqlite3_step():执行SQL语句,遍历结果集,通常用此函数结果同SQLITE_ROW比较,例:
while(sqlite3_step(_stmt) == SQLITE_ROW){
//获取一行中指定列的数据
}
_stmt:同上
result:如果为SQLITE_ROW说明还有其他行没有遍历
-
使用sqlite3_column_*等函数提取字段数据
eg:char *userName = (char *)sqlite3_column_text(_stmt, 1);
int userID = sqlite3_column_int(_stmt, 0);
sqlite3_column_*():获取查询结果中指定的列的数据,数字表示列号,从0开始,而且使用对应的数据类型的函数
0/1:表示要获取的字段所在的序号
userName:UTF8编码的字符串,要转化为NSString类型
-
使用sqlite3_finalize和sqlite3_close函数释放资源
eg:int result = sqlite3_finalize(_stmt);
sqlite3_finalize():销毁_stmt结构体,释放资源
-
修改数据
修改数据:inset、update和delete语句,这3个SQL语句都可以带参数,关于参数的绑定与查询where子句绑定的方式一样,执行修改数据步骤如下:
- 使用sqlite3_open函数打开数据库
- 使用sqlite3_prepare_v2函数预处理SQL语句
- 使用sqlite3_bind_text函数绑定参数
- 使用sqlite3_step函数执行SQL语句
- 使用sqlite3_finalize和sqlite3_close函数释放资源
-
sqlite数据库与SQL Server数据库SQL语句差异
- SQL Server中返回记录数用"top",例:"select top 10 * from [User]"。sqlite中返回记录数用"limit 0,10" 表示从第0条记录开始,往后一共读取10条,例:"select * from [User] order by userId Desc limit 0,10"
- count(DISTINCT column):SQLite在执行如下语句的时候会报错,"select count(DISTINCT watchid) from [watch_item] where watch_item watchid = 1";其原因是SQLite的所有内置函数都不支持DISTINCT限定,所以如果要统计不重复的记录数的时候会出现一些麻烦
- 外连接:虽然SQLite官方声称Left outer join已实现,但还没有Right outer join和full outer join
- getDate():在SQL Server中getDate()返回当前系统日期和时间sqlite中却没有
- exists语句:SQL Server中判断插入,例:if not exists(select * from aa where ids = 5) begin insert into aa(nickname) select 't' end在SQLite中可以这样:insert into aa(nickname) select 't' where not exists(select * from aa where ids = 5)
- 嵌套事务:SQLite仅允许单个活动的事务
- 可更新的视图:SQLite视图是只读的,不能对视图执行delete、insert、update语句,而SQL Server可以
-
SQLite数据库(第三方框架FMDB)
-
创建数据库
-
使用类方法databaseWithPath创建数据库
eg:FMDatabase *database = [FMDatabase databaseWithPath: filePath];
filePath:数据库文件在沙盒目录下的全路径,NSString类型
databaseWithPath:类方法,创建FMDatabase对象,顺便创建数据库
-
使用实例方法open方法打开数据库
eg:BOOL isOpen = [database open];
isOpen:YES—打开成功,NO—打开失败
-
创建表
eg:NSString *createTableSql = @"";
BOOL isSuccess = [database executeUpdate: sql];
sql:创建表的sql语句,NSString类型
isSuccess:YES—执行成功,NO—执行失败
excuteUpdate:执行更新操作的SQL语句
excuteQuery:执行查询操作的SQL语句,返回FMResultSet对象
- 使用[database close]关闭数据库连接
-
-
查询数据
- 创建FMDatabase对象database
- 使用[database open]打开数据库
-
使用executeQuery实例方法查询数据
eg:FMResultSet *res = [database executeQuery: sql]; 或者 FMResultSet *res = [database executeQuery: sql withArgumentsInArray: array];
while([res next]){
int userId = [res intForColumn: @"userId"];
NSString *name = [res stringForColumn: @"name"];
int age = [res intForColumn: @"age"];
}
-
注意
array:SQL语句中"?"所指的参数对象被添加到array中
[res next]:判断res是否还有下一条记录
intForColumn实例方法:获取指定字段名int类型的值
intForColumnIndex实例方法:获取指定索引列int类型的值
- 使用[database close]关闭数据库连接
-
更新数据
- 创建FMDatabase对象database
- 使用[database open]打开数据库
- 使用[database executeUpdate: sql]执行更新的SQL语句
- 使用[database close]关闭数据库连接
-
-
SQLite数据库(CoreData实体数据模型)
-
CoreData核心对象
- NSManagedObject:模型对象,实体对象
- NSManagedObject:模型文件(.xcdatamodeld),记录所有的模型对象及对象的属性
- NSPersistentStoreCoordinator:持久化存储协调者是数据库和程序直接的桥梁
- NSManagedObjectContent:对象管理上下文,用户所有的持久化操作都是通过次对象,再由此对象去调用NSPersistentStoreCoordinator对象执行具体的操作
- 这四大对象的创建见框架自带AppDelegate.m文件
-
saveContext方法
- (void)saveContext{//保存上下文
NSError *error;
if([self.managedObjectContext hasChanges] && ![self.managedObjectContext save: &error]){
NSLog(@"%@",error);
}
}
-
利用CoreData框架
-
增添数据
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
User *user = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:appDelegate.managedObjectContext];//"User":表名
user.userId = @1001;
user.userName = @"用户名";
user.userAge = @(random()%10+20);
[appDelegate saveContext];//saveContext方法具体实现见上节
-
更新数据
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"User"];
request.predicate = [NSPredicate predicateWithFormat:@"userName == '王伟'"];
NSArray *arrResult = [appDelegate.managedObjectContext executeFetchRequest:request error:NULL];
for (User *user in arrResult) {
user.userAge = @30;
[appDelegate.managedObjectContext updatedObjects];
}
[appDelegate saveContext];
-
删除数据、查询数据同更新数据相似,同是先获取相对应的实体对象,然后进行删除、输出、修改数据操作
不同点之删除数据:
for (User *user in arrResult) {
[appDelegate.managedObjectContext deleteObject:user];
}
-
-
注意
-
delete rule选项
Deny:忽略外界约束,照样删除
Nullify:忽略外界约束,照样删除
Cascade:删除外键表记录,主键表记录也会被删除
NoAction:忽略外界约束,照样删除
-
Relationships—Type选项
To One:在实体对象中会生成对一个对象的引用
To Many:在实体对象中会生成对另一个对象的集合的引用,同时增加以下实例方法:
- (void)addReadBookObject:(Book *)value;
- (void)removeReadBookObject:(Book *)value;
- (void)addReadBook:(NSSet<Book *> *)values;
- (void)removeReadBook:(NSSet<Book *> *)values;
-
-