一、iOS应用数据存储的常用方式:
1> XML属性列表(plist)归档
2> Preference(偏好设置)
3> NSKeyedArchiver归档(NSCoding)
4> SQLite3
5> Core Data
1、XML属性列表(plist)归档
#pragma --------- ViewController的.m文件
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end
@implementation ViewController
//存储
- (IBAction)saveBtnClick:(id)sender {
//plist 文件存储 :只有iOS中才有plist文件
//ios中plist文件存储不能有自定义对象
Person *per = [[Person alloc] init];
//plist存储本质,就是帮你生成一个plist文件
//谁才能做plist存储,(数组、字典)
NSArray *arr = @[@"acc",@2];
//File:文件的全路径
//1.先明确文件存储到哪? :应用沙盒的某个文件夹中
//获取应用沙盒路径
// NSString *homePath = NSHomeDirectory();
// NSLog(@"%@",homePath);
//获取到Caches文件夹路径
// NSSearchPathDirectory directory:搜索文件夹
// NSSearchPathDomainMask domainMask:在哪个文件夹内搜索
// BOOL expandTilde:YES:展开路径 NO:不展开路径 ~:代替沙盒路径
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
NSLog(@"%@",cachesPath);
//拼接文件名
NSString *filePath = [cachesPath stringByAppendingPathComponent:@"arr.plist"];
[arr writeToFile:filePath atomically:YES];
}
//读取
- (IBAction)readBtnClick:(id)sender {
//读取:以什么形式存储就以什么方式读取
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
//拼接文件名
NSString *filePath = [cachesPath stringByAppendingPathComponent:@"arr.plist"];
NSArray *arr = [NSArray arrayWithContentsOfFile:filePath];
NSLog(@"%@",arr);
}
@end
#pragma ----------------------
2、 Preference(偏好设置)
#pragma --------- ViewController的.m文件
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
//存储
- (IBAction)save:(id)sender {
//沙盒目录路径
//NSString *homePath = NSHomeDirectory();
//NSLog(@"%@",homePath);
//偏好设置 NSUserDefaults
//获取NSUserDefaults单例对象
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
[defaults setObject:@"xiaoming" forKey:@"name"];
//还可以存BOOL,int类型
[defaults setBool:YES forKey:@"isOn"];
[defaults setInteger:12 forKey:@"num"];
//本质就是存的一个字典
}
//读取
- (IBAction)read:(id)sender {
//获取NSUserDefaults单例对像
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
NSString *name = [defaults objectForKey:@"name"];
NSInteger num = [defaults integerForKey:@"num"];
BOOL isOn = [defaults boolForKey:@"isOn"];
}
@end
#pragma ----------------------
3、 NSKeyedArchiver归档(NSCoding)
#pragma --------- ViewController的.m文件
#import "ViewController.h"
#import "MyClass.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
//点击存储的时候调用
- (IBAction)save:(id)sender {
//存储文件
//1.创建自定义对象
MyClass *myClass = [[MyClass alloc] init];
myClass.num = 13;
myClass.name = @"三二班";
//2.获取路径
//2.1获取沙盒中Caches文件夹
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
//2.2拼接文件名
NSString *filePath = [cachesPath stringByAppendingPathComponent:@"person.data"];
NSLog(@"%@",filePath);
//3.归档
//任意对象都可以使用归档,自定义对象也可以
//Object:需要归档的对象
//file:文件的全路径
[NSKeyedArchiver archiveRootObject:myClass toFile:filePath];
/*
注意:
archiveRootObject:调用自定义对象的 encodeWithCoder:
如果一个自定义对象需要归档,必须要遵守NSCoding协议,且实现协议中的方法
*/
}
//点击读取的时候调用
- (IBAction)read:(id)sender {
//读取文件
//获取路径
//获取沙盒中Caches文件夹
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
//拼接文件名
NSString *filePath = [cachesPath stringByAppendingPathComponent:@"person.data"];
//4.解档
//存进去什么,解出来就是什么
MyClass *myClass = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"myClass:%@",myClass.name);
/*
注意:
unarchiveObjectWithFile:调用自定义对象的 initWithCoder:
如果一个自定义对象需要解档,必须要遵守NSCoding协议,且实现协议中的方法
*/
}
#pragma ----- MyClass的.h文件
#import
@interface MyClass : NSObject
/**
* 人数
*/
@property (nonatomic , assign) NSInteger num;
/**
* 班级名称
*/
@property (nonatomic , strong) NSString *name;
@end
#pragma -------------------------
#pragma ----- MyClass的.m文件
#import "MyClass.h"
@implementation MyClass
//什么作用:告诉系统模型中的哪些属性需要归档
//什么时候调用:把一个自定义的对象归档的时候调用
- (void)encodeWithCoder:(NSCoder *)aCoder{
NSLog(@"aCoder:%@",aCoder);
[aCoder encodeObject:_name forKey:@"name"];
[aCoder encodeInteger:_num forKey:@"num"];
// [aCoder encodeBool:<#(BOOL)#> forKey:<#(nonnull NSString *)#>];
// [aCoder encodeDouble:<#(double)#> forKey:<#(nonnull NSString *)#>];
// [aCoder encodeInt:<#(int)#> forKey:<#(nonnull NSString *)#>];
}
//什么作用:告诉系统模型中的哪些属性需要解档
//什么时候调用:解析一个文件的时候调用
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
#pragma ----- 什么时候调用 [super initWithCoder:aDecoder]?
if (self = [super init]) {
//注意:这里一定要给成员属性赋值
// name
_name = [aDecoder decodeObjectForKey:@"name"];
// num
_num = [aDecoder decodeIntegerForKey:@"num"];
// [aDecoder decodeBoolForKey:<#(nonnull NSString *)#>];
// [aDecoder decodeDoubleForKey:<#(nonnull NSString *)#>];
// [aDecoder decodeIntForKey:<#(nonnull NSString *)#>];
}
return self;
}
@end
#pragma -------------------------
问题?
什么时候调用 [super initWithCoder:aDecoder]?
#pragma ----- 什么时候调用 [super initWithCoder:aDecoder]?
#pragma ----- 只要父类遵守了NSCoding协议,就调用
#pragma ----- 比如:系统调用控件的这个方法解析xib,stroyboard。就会调用 [super initWithCoder:aDecoder]这个方法
#import "RedView.h"
@implementation RedView
//系统调用控件的这个方法帮你解析xib,stroyboard
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
//什么时候调用:[super initWithCoder:aDecoder]
//只要父类遵守了NSCoding协议,就调用
if (self = [super initWithCoder:aDecoder]) {
NSLog(@"initWithCoder:");
}
return self;
}
@end
4、SQLite的应用
4.1 简单说明
在iOS中使用SQLite3,首先要添加库文件libsqlite3.tdb和导入主头文件。
导入头文件,可以使用库中的函数(是纯C语言的)
4.2 详细说明
新建一个项目,在项目的主界面中放四个按钮(分别是,增加、删除、修改、查询)。
1.sqlite3_open(<#const char *filename#>, <#sqlite3 **ppDb#>)函数的一些说明:
(1)作用:把一个文件名称传递给他,它会自动检测这个文件是否存在,如果不存在的话,会自动创建相应的文件(这里为数据库文件,刚创建为空)。
(2)参数:它的第一个参数为文件的名称(需转换为C语言的),第二个参数是数据库的实例,sqlite3 *db;
说明:sqlite3是一种类型,db是数据库的句柄,就是数据库的象征,如果要进行增删改查,就得操作db这个实例。
(3)返回值:它的返回值为int型的,根据函数的返回值可以知道,打开数据库文件是成功还是失败,如果返回值是SQLITE_OK则说明成功,否则为失败。
2.sqlite3_exec(<#sqlite3 *#>, <#const char *sql#>, <#int (*callback)(void *, int, char **, char **)#>, <#void *#>, <#char **errmsg#>)
一般在执行增、删、改的操作都会使用这个函数
2.打开/创建 数据库、创建表
- (void)viewDidLoad {
[super viewDidLoad];
//0.获取数据库文件的路径
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName = [doc stringByAppendingPathComponent:@"student.sqlite"];
NSLog(@"%@",fileName);
//OC字符串传换C语言字符串
const char *filename = fileName.UTF8String;
//1.打开数据库
// open会先判断数据库文件是否存在, 如果不存在sqlite3_open函数会自动创建数据库文件, 然后再打开数据
// open会返回一个int类型的值, 这个值代表着打开数据库是否成功
int result = sqlite3_open(filename, &_db);
if (result == SQLITE_OK) {
NSLog(@"成功打开数据库!");
//2.创建表
//2.1 创建表的SQL语句
const char *createTabelSQL = "CREATE TABLE IF NOT EXISTS t_student (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);";
char *errorMsg = NULL;
//2.2 在 _db 中执行创建表的操作
/*
第1参数:需要执行sql语句的数据库
第2参数:需要执行的sql语句
第3参数:执行完sql语句之后的回调方法
第4参数:回调方法的参数
第5参数:错误信息
*/
result = sqlite3_exec(self.db, createTabelSQL, NULL, NULL, &errorMsg);
if (!errorMsg && result == SQLITE_OK) {
NSLog(@"创建表成功");
}else{
NSLog(@"创建表失败 --错误信息:%s -- 报错的文件:%@ --错误行号:%d",errorMsg,[NSString stringWithUTF8String:__FILE__],__LINE__);
}
}else{
NSLog(@"打开数据库失败!");
}
}
根据打印Log的路径,查看沙盒内创建的数据库文件:
执行后,创表成功,打开创建的表查看:
//4.插入数据 增加
- (IBAction)insertBtnClick {
NSLog(@"%s",__func__);
for (int i=0; i< 20; i++) {
//3. 添加数据的SQL语句
//3.1、拼接SQL语句
NSString *name = [NSString stringWithFormat:@"jack--%d",arc4random_uniform(100)];
int age = arc4random_uniform(20)+30;
NSString *sql = [NSString stringWithFormat:@"INSERT INTO t_student (name, age) VALUES ('%@', %d);", name, age];
const char *c_InsertDBSql = sql.UTF8String;
//3.2、执行SQL语句
char *errorMsg = NULL;
int result = sqlite3_exec(self.db, c_InsertDBSql, NULL, NULL, &errorMsg);
if (!errorMsg && result == SQLITE_OK) {
NSLog(@"插入数据成功");
}else{
NSLog(@"插入数据失败 --错误信息:%s -错误行号:%d",errorMsg,__LINE__);
}
}
}
//查询
- (IBAction)queryBtnClick {
NSLog(@"%s",__func__);
const char *querySql = "SELECT id,name,age FROM t_student WHERE age < 40;";
//查询前的准备工作
/**
sqlite3_prepare_v2(<#sqlite3 *db#>, <#const char *zSql#>, <#int nByte#>, <#sqlite3_stmt **ppStmt#>, <#const char **pzTail#>)
参数1:<#sqlite3 *db#>:数据库实例
参数2:<#const char *zSql#>:执行的SQL语句
参数3:<#int nByte#>:SQL语句的长度(传-1:代表系统会自动计算SQL语句的长度)
参数4:<#sqlite3_stmt **ppStmt#>:用来取数据
参数5:<#const char **pzTail#>:对未使用的部分指向zSql
*/
sqlite3_stmt *stmt = NULL;
int prepareResult = sqlite3_prepare_v2(self.db, querySql, -1, &stmt, NULL);
if (prepareResult == SQLITE_OK) {
NSLog(@"查询语句么问题啦!");
//每调用一次sqlite3_step函数,stmt就会指向下一条记录
while (sqlite3_step(stmt) == SQLITE_ROW) {//找到一条记录
//取出数据
//(1)取出第0列字段的值(int类型的值)
int ID = sqlite3_column_int(stmt, 0);
//(2)取出第1列字段的值(text类型的值)
const unsigned char *name = sqlite3_column_text(stmt, 1);
//(3)取出第2列字段的值(int类型的值)
int age = sqlite3_column_int(stmt, 2);
NSLog(@"%d %s %d\n",ID,name,age);
}
}else{
NSLog(@"查询语句sha'bi'la'ba");
}
}
5、FMDB的应用
// 1.FMDB的基本使用
// Copyright © 2017年 徐sir. All rights reserved.
#import "ViewController.h"
#import "FMDB.h"
@interface ViewController ()
/**
* 数据库
*/
@property (nonatomic , strong) FMDatabase *db;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1.获取数据库文件的路径
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [doc stringByAppendingPathComponent:@"student.sqlite"];
NSLog(@"%@",filePath);
//2.获取数据库
FMDatabase *db = [FMDatabase databaseWithPath:filePath];
//3.创建/打开数据库
if ([db open]) {
NSLog(@"数据库打开成功!");
//4.创建数据库表
BOOL result = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_student (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);"];
if (result) {
NSLog(@"创建表成功!");
}else{
NSLog(@"创建表失败!");
}
}
self.db = db;
}
//添加数据
- (IBAction)insert:(id)sender {
for (int i=0; i< 10; i++) {
NSString *name = [NSString stringWithFormat:@"都比--%d", arc4random_uniform(100)];
// executeUpdate : 不确定的参数用?来占位
[self.db executeUpdate:@"INSERT INTO t_student (name,age) VALUES(?,?)",name,@(arc4random_uniform(40))];
// [self.db executeUpdate:@"INSERT INTO t_student (name,age) VALUES(?,?)" withArgumentsInArray:@[name,@(arc4random_uniform(40))]];
// executeUpdateWithFormat : 不确定的参数用%@、%d等来占位
// [self.db executeUpdateWithFormat:@"INSERT INTO t_student (name,age) VALUES(%@,%@)",name,@(arc4random_uniform(40))];
}
}
//删除数据
- (IBAction)delete:(id)sender {
// [self.db executeUpdate:@"DELETE FROM t_student"];
[self.db executeUpdate:@"DROP TABLE IF EXISTS t_student;"];
[self.db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_student (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);"];
}
//修改
- (IBAction)update:(id)sender {
NSString *updateSql = [NSString stringWithFormat:@"UPDATE t_student SET name = '%@'",@"都比"];
[self.db executeUpdate:updateSql];
}
//执行数据库中的查询数据
- (IBAction)select:(id)sender {
//1.执行查询语句
FMResultSet *resultSet = [self.db executeQuery:@"SELECT *FROM t_student"];
//2.遍历结果
while([resultSet next]){
int ID = [resultSet intForColumn:@"id"];
NSString *name = [resultSet stringForColumn:@"name"];
int age = [resultSet intForColumn:@"age"];
NSLog(@"%d %@ %d",ID,name,age);
}
}
@end
// 2.FMDB数据库队列 -- FMDatabaseQueue
// Copyright © 2017年 徐sir. All rights reserved.
#import "ViewController.h"
#import "FMDB.h"
@interface ViewController ()
/** FMDB数据库队列 */
@property (nonatomic , strong) FMDatabaseQueue *dbQueue;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1.获取数据库文件的路径
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [doc stringByAppendingPathComponent:@"person.sqlite"];
NSLog(@"%@",filePath);
//2.获取数据库
FMDatabaseQueue *dbQueue = [FMDatabaseQueue databaseQueueWithPath:filePath];
//3.创建/打开数据库
[dbQueue inDatabase:^(FMDatabase *db) {
//4.创建数据库表
BOOL result = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_person (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL);"];
if (result) {
NSLog(@"创建表成功!");
}else{
NSLog(@"创建表失败!");
}
}];
self.dbQueue = dbQueue;
}
//增加数据
- (IBAction)insertBtnClick:(id)sender {
[self.dbQueue inDatabase:^(FMDatabase *db) {
BOOL result = [db executeUpdate:@"INSERT INTO t_person (name,age) VALUES(?,?);",@"都比",@12];
if (result) {
NSLog(@"数据增加成功");
}else{
NSLog(@"数据增加失败");
}
}];
//事物回滚
/*
[self.dbQueue inDatabase:^(FMDatabase *db) {
[db beginTransaction];
[db executeUpdate:@"INSERT INTO t_person (name,age) VALUES(?,?);",@"都比1",@12];
[db executeUpdate:@"INSERT INTO t_person (name,age) VALUES(?,?);",@"kaxin",@13];
[db executeUpdate:@"INSERT INTO t_person (name,age) VALUES(?,?);",@"都比1",@12];
[db executeUpdate:@"INSERT INTO t_person (name,age) VALUES(?,?);",@"都比1",@12];
[db commit];
}];
*/
//事物回滚的另一种方式
/*
[self.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"INSERT INTO t_person (name,age) VALUES(?,?);",@"都比1",@12];
[db executeUpdate:@"INSERT INTO t_person (name,age) VALUES(?,?);",@"kaxin",@13];
[db executeUpdate:@"INSERT INTO t_person (name,age) VALUES(?,?);",@"都比1",@12];
[db executeUpdate:@"INSERT INTO t_person (name,age) VALUES(?,?);",@"都比1",@12];
}];
*/
}
//删除数据
- (IBAction)deleteBtnClick:(id)sender {
}
//修改数据
- (IBAction)updateBtnClick:(id)sender {
}
//查询数据
- (IBAction)selectBtnClick:(id)sender {
[self.dbQueue inDatabase:^(FMDatabase *db) {
//1.执行查询语句
FMResultSet *resultSet = [db executeQuery:@"SELECT *FROM t_person"];
//2.遍历结果
while([resultSet next]){
int ID = [resultSet intForColumn:@"id"];
NSString *name = [resultSet stringForColumn:@"name"];
int age = [resultSet intForColumn:@"age"];
NSLog(@"%d %@ %d",ID,name,age);
}
}];
}
@end