一、文件管理
1.1 iOS沙盒机制: iOS应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒,所以所有的非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文本文件 等。
• 每个应用程序都有⾃己的存储空间
• 应用程序不能翻过⾃己的围墙去访问别的存储空间的内容
• 应用程序请求的数据都要通过权限检测,假如不符合条件的话,不会被放⾏。
1.1、文件保存的目录结构
iOS的文件存储采用的是沙盒目录机制,没有SD卡保存的概念。一个APP安装成功后就会对应一个文件目录这个目录就叫沙盒。这个APP的所有文件都会保存在这个目录中,每个APP沙盒独立互相不能访问目录,沙盒目录中包含Documents、Library、Tmp三个子目录。结构如下:
对于Document文件夹目录路径的获取,API提供了这么一种方法:
directory
:要查找的目录
domainMask
:从哪个主目录中获取(通常是沙盒目录)
expandTilde
: 是否返回完整的路径
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"%@",documents);
1.1.1 Library
Library
:存储程序的默认设置或其它状态信息;(iTunes备份和恢复的时候会包含此目录) Library/Caches:存放缓存⽂件,iTunes不会备份此目录,此目录下文件不会在应⽤退出删除
获取Library目录路径:
NSString *library = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"%@",library);
1.1.2 获取缓存目录Caches路径
NSString *cacher = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"%@",cacher);
1.1.3 tmp
提供一个即时创建临时⽂件的地方。(系统重启后会删除tmp里的所有文件)
获取Tem目录路径:
NSString *tem = NSTemporaryDirectory();
NSLog(@"%@",tem);
二、属性列表
2.1 属性列表的基本概念
属性列表是一种明文的轻量级存储方式,其存储格式有多种,最常规格式为XML格式。在我们创建一个新的项目的时候,Xcode会自动生成一个info.plist文件用来存储项目的部分系统设置。plist只能用数组(NSArray)或者字典(NSDictionary)进行读取,由于属性列表本身不加密,所以安全性几乎可以说为零。因为,属性列表正常用于存储少量的并且不重要的数据。
2.2 使用NSUserDefaults保存为pist文件
在程序启动后,系统会自动创建一个NSUserDefaults的单例对象,我们可以获取这个单例来存储少量的数据,它会将输出存储在.plist格式的文件中。其优点是像字典一样的赋值方式方便简单,但缺点是无法存储自定义的数据。
获得NSUserDefaults系统提供的plist文件:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
设值:
[userDefaults setObject:@100 forKey:@"id"];
[userDefaults setBool:YES forKey:@"isLoad"];
[userDefaults setDouble:3.8408 forKey:@"price"];
数据同步(即使更新数据):
[userDefaults synchronize];
取值:
id value = [userDefaults objectForKey:@"id"];
NSLog(@"%@",value);
修改:
[userDefaults setObject:@2000 forKey:@"id"];
[userDefaults synchronize];
三、对象归档
3.1 对象归档的基本概念
与属性列表相反,同样作为轻量级存储的持久化方案,数据归档是进行加密处理的,数据在经过归档处理会转换成二进制数据,所以安全性要远远高于属性列表。另外使用归档方式,我们可以将复杂的对象写入文件中,并且不管添加多少对象,将对象写入磁盘的方式都是一样的。
使用NSKeyedArchiver对自定义的数据进行序列化,并且保存在沙盒目录下。使用这种归档的前提是让存储的数据模型遵守NSCoding协议并且实现其两个协议方法。(当然,如果为了更加安全的存储,也可以遵守NSSecureCoding协议,这是iOS6之后新增的特性)
• 概述: 对象归档是将对象归档以⽂文件的形式保存到磁盘中(也称为序列化,持久化),使用的时候读取该⽂文件的保存路径读取⽂文件的内容(也称为接档,反序列化), 对象归档的⽂文件是保密的,在磁盘上⽆无法查看⽂文件中的内容,⽽而属性列表是明⽂文的,可 以查看。
3.2 对象归档的方式
- 简单归档
- 对多个对象进⾏归档
- 对自定义对象进⾏归档
归档类NSKeyedArchiver和反归档类NSKeyedUnarchiver总与NSData关联在一起。NSData封装了字节数据的缓存类,提供类读写数据的方法,具体如下所示:
dataWithContentsOfFile:
他是静态工厂方法,用于从文件读取数据来创建NSData对象,
dataWithContentsOfFile:options:error:
它是静态工厂方法,用于从文件读取数据来创建NSData对象,options参数是指定读取数据选项,error参数是返回读取数据的错误。
initWithContentsOfFile:options:error:
它是实例构造函数,同上一个方法一样。
writeToFile:options:error:
将NSData对象写入文件,atomically参数是否写入辅助文件,NO时数据直接写入目标文件路径,ture时数据写入辅助文件,写入成功后将辅助文件路径改名为目标文件路径。当目标文件已经存在时,atomically设置为false,可以防止系统崩溃所导致旧文件的破坏。
归档(编码保存):将数据以二进制的形式保存,常用的NSString、NSArray、NSDictionary.... 可以直接归档保存,因为它们都实现了 <NSCoding> 协议 : NSKeyedArchiver 归档代码:
BOOL isSuccess = [NSKeyedArchiver archiveRootObject:user toFile:filePath];
归档过程是使用NSKeyedArchiver
对象归档数据,具体过程为:首先将归档数据写入NSData对象,最后再将NSData对象写入归档文件,反归档过程是从归档文件中读取NSData对象,再利用NSKeyedUnarchiver
对象从NSData对象中反归档数据。
归档协议方法实现:
#pragma mark - <NSCoing> 对属性编码和解码
// 编码
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_userName forKey:@"userName"];
[aCoder encodeObject:_password forKey:@"password"];
}
// 解码
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self ) {
self.userName = [aDecoder decodeObjectForKey:@"userName"];
self.password = [aDecoder decodeObjectForKey:@"password"];
}
return self;
}
解归档(读取解码): NSKeyedUnarchiver:
id object = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
User *user_ = (User *)object;
四、SQLite数据库
- SQLite是一个轻量级、跨平台的小型数据库,其拥有可移植性高、有着和MySql几乎相同的数据库语句以及无需服务器即可使用的优点:
1、可以存储大量的数据,存储和检索的速度非常快;
2、能对数据进行大量的聚合,这样比起使用对象来进行这些操作要快。
当然,它也具有明显的缺点:
1、它没有提供数据库的创建方式;
2、它基于C语言框架设计,没有面向对象的API,所以使用起来比较麻烦;
3、复杂的数据模型的数据建表相对而言比较麻烦。
当然,我们也可以使用基于SQLite封装的开源数据库FMDB来减少使用SQLite的工作量
4.1 创建数据库
// 1、创建数据库指针,相当于创建一个数据库对象。
sqlite3 *_sqlDB;
// 2、创建数据库保存的路径
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *dataPath = [docPath stringByAppendingFormat:@"/%@.sqlite",dbName];
// 3、打开数据库
int result = sqlite3_open([dataPath UTF8String], &_sqlDB);
if (result == SQLITE_OK) {
NSLog(@"创建/打开成功!");
} else {
NSLog(@"创建/打开失败!");
// 4、关闭数据库
sqlite3_close(_sqlDB);
}
代码解析:
1、数据库扩展名称为.sqlite
2、sqlite3_open()函数包含两个参数,第一个为数据库的路径因为参数的类型是 char *类型,所有要用[dataPath UTF8String]进行类型转换,第二个为数据库sqlite3指针的地址。
3、如果函数的返回值等于 SQLITE_OK(宏) 表示成功。
4、数据库打开失败或者使用完后都要调用sqlite3_close()函数,关闭数据库释放资源。
4.2 创建数据库表
#pragma mark - 创建数据库表
- (void)createTable {
NSString *sql = @"CREATE TABLE IF NOT EXISTS User('userID' TEXT, 'userName' TEXT, 'userAge' INTEGER)";
// sqlite3_exec() 执行 sql语句 函数
int result = sqlite3_exec(_sqliteDB,[sql UTF8String],NULL,NULL,NULL);
if (result == SQLITE_OK) {
NSLog(@"创建表成功");
} else {
NSLog(@"创建失败");
}
}
4.3 插入数据
#pragma mark - 数据库表中插入数据
- (void)insertValues {
NSString *userID = @"100015";
NSString *userName = @"李杨";
int age = 20;
NSString *sql = [NSString stringWithFormat:@"INSERT INTO User('userID','userName','userAge') VALUES('%@','%@',%d)",userID,userName,age];
// sqlite3_exec() 执行 sql 函数
int result = sqlite3_exec(_sqliteDB,[sql UTF8String],NULL,NULL,NULL);
if (result == SQLITE_OK) {
NSLog(@"插入数据成功");
} else {
NSLog(@"失败");
}
}
4.4 添加字段 (列)
#pragma mark - 添加字段(列)
- (void)addColumn {
NSString *columnName = @"userSex TEXT";
NSString *sql = [NSString stringWithFormat:@"ALTER TABLE User ADD COLUMN %@",columnName];
// sqlite3_exec() 执行 sql 函数
int result = sqlite3_exec(_sqliteDB,[sql UTF8String],NULL,NULL,NULL);
if (result == SQLITE_OK) {
NSLog(@"添加字段成功");
} else {
NSLog(@"失败");
}
}
代码解析:
1、sql语句:INSERT INTO Person (字段1,字段2...) VALUES (数据1,数据2...)"
表示在Person表里的 pID,pName,pSex,pTel 字段中,分别插入对应的值。
2、也sqlite3_exec()执行sql语句,非查询的sql语句都用这个函数执行
4.5 更新数据
#pragma mark - 更新数据
- (void)updateValues {
NSString *userSex = @"男";
NSString *userName = @"电风扇";
NSString *sql = [NSString stringWithFormat:@"UPDATE User SET userSex = '%@' WHERE userName = '%@'",userSex,userName];
// sqlite3_exec() 执行 sql 函数
int result = sqlite3_exec(_sqliteDB,[sql UTF8String],NULL,NULL,NULL);
if (result == SQLITE_OK) {
NSLog(@"修改数据成功成功");
} else {
NSLog(@"失败");
}
}
4.6 删除数据
#pragma mark - 删除数据
- (void)deleteRow {
NSString *userName = @"电风扇";
NSString *sql = [NSString stringWithFormat:@"DELETE FROM User WHERE userName = '%@'",userName];
// sqlite3_exec() 执行 sql 函数
int result = sqlite3_exec(_sqliteDB,[sql UTF8String],NULL,NULL,NULL);
if (result == SQLITE_OK) {
NSLog(@"删除数据成功");
} else {
NSLog(@"失败");
}
}
4.7 查询数据
#pragma mark - 查询数据
- (void)selectDatas {
//1、创建sql语句结构体指针(可以理解为sql语句对象)
sqlite3_stmt *_stmt;
NSString *key = @"张";
NSString *sql = [NSString stringWithFormat:@"SELECT *FROM User WHERE userName LIKE '%%%@%%'",key];
// 2、编译查询语句
int result = sqlite3_prepare_v2(_sqliteDB,[sql UTF8String],-1,&_stmt,NULL);
if (result == SQLITE_OK) {
NSLog(@"sql语句编译通过");
NSMutableArray *users = [NSMutableArray array];
// 3、执行查询语句,== SQLITE_ROW 表示还有下一条数据
while (sqlite3_step(_stmt) == SQLITE_ROW) {
/* 4、获取每一行数据对应的字段(列)
sqlite3_column_text() 对应 TEXT 类型
sqlite3_column_int() 对应 INTERER类型
*/
char *userID = (char *)sqlite3_column_text(_stmt,0);
char *userName = (char *)sqlite3_column_text(_stmt,1);
int userAge = sqlite3_column_int(_stmt,2);
char *userSex = (char *)sqlite3_column_text(_stmt,3);
NSString *userID_str = [NSString stringWithUTF8String:userID];
NSString *userName_str = [NSString stringWithUTF8String:userName];
NSString *userSex_str = [NSString stringWithUTF8String:userSex];
// 把查询到的数据保存为 model 对象
User *user = [[User alloc] init];
user.userID = userID_str;
user.userName = userName_str;
user.userSex = userSex_str;
user.userAge = userAge;
[users addObject:user];
NSLog(@"%@, %@, %d,%@",userID_str,userName_str,userAge,userSex_str);
}
} else {
NSLog(@"失败");
}
}
代码解析:
1、sql语句:SELECT * FROM 表名 WHERE 查询条件
,表示从Person表格中查询pAge大于20的所有行。
2、sqlite3_stmt 相当于sql语句的对象,执行语句、获取查询到的取都是对这个对象进行操作。
3、sqlite3_prepare_v2(),sql语句预处理操作,在执行语句前做的事相当于编译 sql 语句。包含五个参数,第一个数据库指针,第二个sql语句,第三个能从sql语句中获取的最大字节数,使用-1表示整个sql语句。第四个sqlite3_stmt对象地址,第五个指向未使用部分的 sql 语句的指针,如果为前面为-1就使用NULL。
4、sqlite3_step(),执行 sql 语句,将编译好的sql语句执行,这个函数可能会多次执行,因为查询的数据可能有多条,所有要一个循环语句一起使用。循环条件是sqlite3_step(stmt) == SQLITE_ROW 表示还有其他行数据。
5、sqlite3_column_text(stmt, 0),获取查询到的一行结果中对应列的数据,text表示字符串类型 返回char *类型,0表示获取第0列的数据。这个函数还有int、double等类型,返回值得类型也对应。选择列对应的数据类型的函数。
6、使用数据库查询到的数据通常要保存到 mode层中,方便使用,所有创建了一个Person对象保存对应的数据。
7、sqlite3_finalize(),使用完查询对象后要释放。