一、iOS應(yīng)用數(shù)據(jù)存儲(chǔ)的常用方式:
1> XML屬性列表(plist)歸檔
2> Preference(偏好設(shè)置)
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
//存儲(chǔ)
- (IBAction)saveBtnClick:(id)sender {
//plist 文件存儲(chǔ) :只有iOS中才有plist文件
//ios中plist文件存儲(chǔ)不能有自定義對象
Person *per = [[Person alloc] init];
//plist存儲(chǔ)本質(zhì),就是幫你生成一個(gè)plist文件
//誰才能做plist存儲(chǔ),(數(shù)組、字典)
NSArray *arr = @[@"acc",@2];
//File:文件的全路徑
//1.先明確文件存儲(chǔ)到哪? :應(yīng)用沙盒的某個(gè)文件夾中
//獲取應(yīng)用沙盒路徑
// NSString *homePath = NSHomeDirectory();
// NSLog(@"%@",homePath);
//獲取到Caches文件夾路徑
// NSSearchPathDirectory directory:搜索文件夾
// NSSearchPathDomainMask domainMask:在哪個(gè)文件夾內(nèi)搜索
// 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 {
//讀取:以什么形式存儲(chǔ)就以什么方式讀取
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
//拼接文件名
NSString *filePath = [cachesPath stringByAppendingPathComponent:@"arr.plist"];
NSArray *arr = [NSArray arrayWithContentsOfFile:filePath];
NSLog(@"%@",arr);
}
@end
#pragma ----------------------

屏幕快照 2017-05-02 上午10.51.07.png
2、 Preference(偏好設(shè)置)
#pragma --------- ViewController的.m文件
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
//存儲(chǔ)
- (IBAction)save:(id)sender {
//沙盒目錄路徑
//NSString *homePath = NSHomeDirectory();
//NSLog(@"%@",homePath);
//偏好設(shè)置 NSUserDefaults
//獲取NSUserDefaults單例對象
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
[defaults setObject:@"xiaoming" forKey:@"name"];
//還可以存BOOL,int類型
[defaults setBool:YES forKey:@"isOn"];
[defaults setInteger:12 forKey:@"num"];
//本質(zhì)就是存的一個(gè)字典
}
//讀取
- (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 ----------------------

屏幕快照 2017-05-02 下午2.18.10.png
3、 NSKeyedArchiver歸檔(NSCoding)
#pragma --------- ViewController的.m文件
#import "ViewController.h"
#import "MyClass.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
//點(diǎn)擊存儲(chǔ)的時(shí)候調(diào)用
- (IBAction)save:(id)sender {
//存儲(chǔ)文件
//1.創(chuàng)建自定義對象
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:需要?dú)w檔的對象
//file:文件的全路徑
[NSKeyedArchiver archiveRootObject:myClass toFile:filePath];
/*
注意:
archiveRootObject:調(diào)用自定義對象的 encodeWithCoder:
如果一個(gè)自定義對象需要?dú)w檔,必須要遵守NSCoding協(xié)議,且實(shí)現(xiàn)協(xié)議中的方法
*/
}
//點(diǎn)擊讀取的時(shí)候調(diào)用
- (IBAction)read:(id)sender {
//讀取文件
//獲取路徑
//獲取沙盒中Caches文件夾
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
//拼接文件名
NSString *filePath = [cachesPath stringByAppendingPathComponent:@"person.data"];
//4.解檔
//存進(jìn)去什么,解出來就是什么
MyClass *myClass = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"myClass:%@",myClass.name);
/*
注意:
unarchiveObjectWithFile:調(diào)用自定義對象的 initWithCoder:
如果一個(gè)自定義對象需要解檔,必須要遵守NSCoding協(xié)議,且實(shí)現(xiàn)協(xié)議中的方法
*/
}

屏幕快照 2017-05-02 下午3.46.41.png
#pragma ----- MyClass的.h文件
#import <Foundation/Foundation.h>
@interface MyClass : NSObject<NSCoding>
/**
* 人數(shù)
*/
@property (nonatomic , assign) NSInteger num;
/**
* 班級(jí)名稱
*/
@property (nonatomic , strong) NSString *name;
@end
#pragma -------------------------
#pragma ----- MyClass的.m文件
#import "MyClass.h"
@implementation MyClass
//什么作用:告訴系統(tǒng)模型中的哪些屬性需要?dú)w檔
//什么時(shí)候調(diào)用:把一個(gè)自定義的對象歸檔的時(shí)候調(diào)用
- (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 *)#>];
}
//什么作用:告訴系統(tǒng)模型中的哪些屬性需要解檔
//什么時(shí)候調(diào)用:解析一個(gè)文件的時(shí)候調(diào)用
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
#pragma ----- 什么時(shí)候調(diào)用 [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 -------------------------
問題?
什么時(shí)候調(diào)用 [super initWithCoder:aDecoder]?
#pragma ----- 什么時(shí)候調(diào)用 [super initWithCoder:aDecoder]?
#pragma ----- 只要父類遵守了NSCoding協(xié)議,就調(diào)用
#pragma ----- 比如:系統(tǒng)調(diào)用控件的這個(gè)方法解析xib,stroyboard。就會(huì)調(diào)用 [super initWithCoder:aDecoder]這個(gè)方法
#import "RedView.h"
@implementation RedView
//系統(tǒng)調(diào)用控件的這個(gè)方法幫你解析xib,stroyboard
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
//什么時(shí)候調(diào)用:[super initWithCoder:aDecoder]
//只要父類遵守了NSCoding協(xié)議,就調(diào)用
if (self = [super initWithCoder:aDecoder]) {
NSLog(@"initWithCoder:");
}
return self;
}
@end
4、SQLite的應(yīng)用
4.1 簡單說明
在iOS中使用SQLite3,首先要添加庫文件libsqlite3.tdb和導(dǎo)入主頭文件。


屏幕快照 2017-05-05 下午5.09.25.png

屏幕快照 2017-05-05 下午5.09.39.png
導(dǎo)入頭文件,可以使用庫中的函數(shù)(是純C語言的)

屏幕快照 2017-05-05 下午5.21.19.png
4.2 詳細(xì)說明
新建一個(gè)項(xiàng)目,在項(xiàng)目的主界面中放四個(gè)按鈕(分別是,增加、刪除、修改、查詢)。

屏幕快照 2017-05-05 下午5.29.13.png
1.sqlite3_open(<#const char *filename#>, <#sqlite3 **ppDb#>)函數(shù)的一些說明:
(1)作用:把一個(gè)文件名稱傳遞給他,它會(huì)自動(dòng)檢測這個(gè)文件是否存在,如果不存在的話,會(huì)自動(dòng)創(chuàng)建相應(yīng)的文件(這里為數(shù)據(jù)庫文件,剛創(chuàng)建為空)。
(2)參數(shù):它的第一個(gè)參數(shù)為文件的名稱(需轉(zhuǎn)換為C語言的),第二個(gè)參數(shù)是數(shù)據(jù)庫的實(shí)例,sqlite3 *db;
說明:sqlite3是一種類型,db是數(shù)據(jù)庫的句柄,就是數(shù)據(jù)庫的象征,如果要進(jìn)行增刪改查,就得操作db這個(gè)實(shí)例。
(3)返回值:它的返回值為int型的,根據(jù)函數(shù)的返回值可以知道,打開數(shù)據(jù)庫文件是成功還是失敗,如果返回值是SQLITE_OK則說明成功,否則為失敗。
2.sqlite3_exec(<#sqlite3 *#>, <#const char *sql#>, <#int (*callback)(void *, int, char **, char **)#>, <#void *#>, <#char **errmsg#>)
一般在執(zhí)行增、刪、改的操作都會(huì)使用這個(gè)函數(shù)
2.打開/創(chuàng)建 數(shù)據(jù)庫、創(chuàng)建表
- (void)viewDidLoad {
[super viewDidLoad];
//0.獲取數(shù)據(jù)庫文件的路徑
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName = [doc stringByAppendingPathComponent:@"student.sqlite"];
NSLog(@"%@",fileName);
//OC字符串傳換C語言字符串
const char *filename = fileName.UTF8String;
//1.打開數(shù)據(jù)庫
// open會(huì)先判斷數(shù)據(jù)庫文件是否存在, 如果不存在sqlite3_open函數(shù)會(huì)自動(dòng)創(chuàng)建數(shù)據(jù)庫文件, 然后再打開數(shù)據(jù)
// open會(huì)返回一個(gè)int類型的值, 這個(gè)值代表著打開數(shù)據(jù)庫是否成功
int result = sqlite3_open(filename, &_db);
if (result == SQLITE_OK) {
NSLog(@"成功打開數(shù)據(jù)庫!");
//2.創(chuàng)建表
//2.1 創(chuàng)建表的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 中執(zhí)行創(chuàng)建表的操作
/*
第1參數(shù):需要執(zhí)行sql語句的數(shù)據(jù)庫
第2參數(shù):需要執(zhí)行的sql語句
第3參數(shù):執(zhí)行完sql語句之后的回調(diào)方法
第4參數(shù):回調(diào)方法的參數(shù)
第5參數(shù):錯(cuò)誤信息
*/
result = sqlite3_exec(self.db, createTabelSQL, NULL, NULL, &errorMsg);
if (!errorMsg && result == SQLITE_OK) {
NSLog(@"創(chuàng)建表成功");
}else{
NSLog(@"創(chuàng)建表失敗 --錯(cuò)誤信息:%s -- 報(bào)錯(cuò)的文件:%@ --錯(cuò)誤行號(hào):%d",errorMsg,[NSString stringWithUTF8String:__FILE__],__LINE__);
}
}else{
NSLog(@"打開數(shù)據(jù)庫失?。?);
}
}
根據(jù)打印Log的路徑,查看沙盒內(nèi)創(chuàng)建的數(shù)據(jù)庫文件:

屏幕快照 2017-05-05 下午5.42.39.png
執(zhí)行后,創(chuàng)表成功,打開創(chuàng)建的表查看:

屏幕快照 2017-05-05 下午5.45.43.png
//4.插入數(shù)據(jù) 增加
- (IBAction)insertBtnClick {
NSLog(@"%s",__func__);
for (int i=0; i< 20; i++) {
//3. 添加數(shù)據(jù)的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、執(zhí)行SQL語句
char *errorMsg = NULL;
int result = sqlite3_exec(self.db, c_InsertDBSql, NULL, NULL, &errorMsg);
if (!errorMsg && result == SQLITE_OK) {
NSLog(@"插入數(shù)據(jù)成功");
}else{
NSLog(@"插入數(shù)據(jù)失敗 --錯(cuò)誤信息:%s -錯(cuò)誤行號(hào):%d",errorMsg,__LINE__);
}
}
}
//查詢
- (IBAction)queryBtnClick {
NSLog(@"%s",__func__);
const char *querySql = "SELECT id,name,age FROM t_student WHERE age < 40;";
//查詢前的準(zhǔn)備工作
/**
sqlite3_prepare_v2(<#sqlite3 *db#>, <#const char *zSql#>, <#int nByte#>, <#sqlite3_stmt **ppStmt#>, <#const char **pzTail#>)
參數(shù)1:<#sqlite3 *db#>:數(shù)據(jù)庫實(shí)例
參數(shù)2:<#const char *zSql#>:執(zhí)行的SQL語句
參數(shù)3:<#int nByte#>:SQL語句的長度(傳-1:代表系統(tǒng)會(huì)自動(dòng)計(jì)算SQL語句的長度)
參數(shù)4:<#sqlite3_stmt **ppStmt#>:用來取數(shù)據(jù)
參數(shù)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(@"查詢語句么問題啦!");
//每調(diào)用一次sqlite3_step函數(shù),stmt就會(huì)指向下一條記錄
while (sqlite3_step(stmt) == SQLITE_ROW) {//找到一條記錄
//取出數(shù)據(jù)
//(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的應(yīng)用
// 1.FMDB的基本使用
// Copyright ? 2017年 徐sir. All rights reserved.
#import "ViewController.h"
#import "FMDB.h"
@interface ViewController ()
/**
* 數(shù)據(jù)庫
*/
@property (nonatomic , strong) FMDatabase *db;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1.獲取數(shù)據(jù)庫文件的路徑
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [doc stringByAppendingPathComponent:@"student.sqlite"];
NSLog(@"%@",filePath);
//2.獲取數(shù)據(jù)庫
FMDatabase *db = [FMDatabase databaseWithPath:filePath];
//3.創(chuàng)建/打開數(shù)據(jù)庫
if ([db open]) {
NSLog(@"數(shù)據(jù)庫打開成功!");
//4.創(chuàng)建數(shù)據(jù)庫表
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(@"創(chuàng)建表成功!");
}else{
NSLog(@"創(chuàng)建表失?。?);
}
}
self.db = db;
}
//添加數(shù)據(jù)
- (IBAction)insert:(id)sender {
for (int i=0; i< 10; i++) {
NSString *name = [NSString stringWithFormat:@"都比--%d", arc4random_uniform(100)];
// executeUpdate : 不確定的參數(shù)用?來占位
[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 : 不確定的參數(shù)用%@、%d等來占位
// [self.db executeUpdateWithFormat:@"INSERT INTO t_student (name,age) VALUES(%@,%@)",name,@(arc4random_uniform(40))];
}
}
//刪除數(shù)據(jù)
- (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];
}
//執(zhí)行數(shù)據(jù)庫中的查詢數(shù)據(jù)
- (IBAction)select:(id)sender {
//1.執(zhí)行查詢語句
FMResultSet *resultSet = [self.db executeQuery:@"SELECT *FROM t_student"];
//2.遍歷結(jié)果
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數(shù)據(jù)庫隊(duì)列 -- FMDatabaseQueue
// Copyright ? 2017年 徐sir. All rights reserved.
#import "ViewController.h"
#import "FMDB.h"
@interface ViewController ()
/** FMDB數(shù)據(jù)庫隊(duì)列 */
@property (nonatomic , strong) FMDatabaseQueue *dbQueue;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1.獲取數(shù)據(jù)庫文件的路徑
NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [doc stringByAppendingPathComponent:@"person.sqlite"];
NSLog(@"%@",filePath);
//2.獲取數(shù)據(jù)庫
FMDatabaseQueue *dbQueue = [FMDatabaseQueue databaseQueueWithPath:filePath];
//3.創(chuàng)建/打開數(shù)據(jù)庫
[dbQueue inDatabase:^(FMDatabase *db) {
//4.創(chuàng)建數(shù)據(jù)庫表
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(@"創(chuàng)建表成功!");
}else{
NSLog(@"創(chuàng)建表失?。?);
}
}];
self.dbQueue = dbQueue;
}
//增加數(shù)據(jù)
- (IBAction)insertBtnClick:(id)sender {
[self.dbQueue inDatabase:^(FMDatabase *db) {
BOOL result = [db executeUpdate:@"INSERT INTO t_person (name,age) VALUES(?,?);",@"都比",@12];
if (result) {
NSLog(@"數(shù)據(jù)增加成功");
}else{
NSLog(@"數(shù)據(jù)增加失敗");
}
}];
//事物回滾
/*
[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];
}];
*/
}
//刪除數(shù)據(jù)
- (IBAction)deleteBtnClick:(id)sender {
}
//修改數(shù)據(jù)
- (IBAction)updateBtnClick:(id)sender {
}
//查詢數(shù)據(jù)
- (IBAction)selectBtnClick:(id)sender {
[self.dbQueue inDatabase:^(FMDatabase *db) {
//1.執(zhí)行查詢語句
FMResultSet *resultSet = [db executeQuery:@"SELECT *FROM t_person"];
//2.遍歷結(jié)果
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