如何存儲(chǔ)數(shù)據(jù) :
文件:1、沙盒? ?2、Plist? ? 3、NSKeyedArchiver歸檔 / NSKeyedUnarchiver解檔
NSUserDefaults
數(shù)據(jù)庫 :1、SQLite3? 2、FMDB? 3、Core Data
沙盒:
iOS本地化存儲(chǔ)的數(shù)據(jù)保存在沙盒中。
Documents:iTunes會(huì)備份該目錄。一般用來存儲(chǔ)需要持久化的數(shù)據(jù)。
Library/Caches:緩存,iTunes不會(huì)備份該目錄。內(nèi)存不足時(shí)會(huì)被清除,應(yīng)用沒有運(yùn)行時(shí),可能會(huì)被清除,。一般存儲(chǔ)體積大、不需要備份的非重要數(shù)據(jù)。
Library/Preference:iTunes同會(huì)備份該目錄,可以用來存儲(chǔ)一些偏好設(shè)置。
tmp: iTunes不會(huì)備份這個(gè)目錄,用來保存臨時(shí)數(shù)據(jù),應(yīng)用退出時(shí)會(huì)清除該目錄下的數(shù)據(jù)
獲取沙盒文件:
// 獲取Documents目錄的路徑
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
// 得到Document目錄下的fileName文件的路徑
NSString *filePath = [documentPath stringByAppendingPathComponent:fileName];
//獲取Library/Caches目錄路徑
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
//獲取Library/Caches目錄下的fileName文件路徑
NSString *filePath = [path stringByAppendingPathComponent:fileName];
//獲取temp路徑
NSString *tmp = NSTemporaryDirectory();
//獲取temp下fileName文件的路徑
NSString *filePath = [tmp stringByAppendingPathComponent:fileName];
NSDocumentDirectory: 第一個(gè)參數(shù)代表要查找哪個(gè)文件,是一個(gè)枚舉。
NSUserDomainMask: 也是一個(gè)枚舉,表示搜索的范圍限制于當(dāng)前應(yīng)用的沙盒目錄。。
YES (expandTilde): 第三個(gè)參數(shù)是一個(gè)BOOL值。iOS中主目錄的全寫形式是/User/userName,這個(gè)參數(shù)填YES就表示全寫,填NO就是寫成‘‘~’’。
plist:
可以把字典或數(shù)組直接寫入到文件中。另外,NSString、NSData、NSNumber等類型,也可以使用writeToFile:atomically:方法直接將對(duì)象寫入文件中,只是Type為空。
存儲(chǔ):
// 要保存的數(shù)據(jù)
NSDictionary *dict = [NSDictionary dictionaryWithObject:@"iOS cookbook" forKey:@"bookName"];
// 獲取路徑.
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
NSString *filePath = [documentPath stringByAppendingPathComponent:@"test.plist"];
// 寫入數(shù)據(jù)
[dict writeToFile:filePath atomically:YES];
讀取:
// 文件路徑
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
NSString *filePath = [documentPath stringByAppendingPathComponent:@"test.plist"];
// 解析數(shù)據(jù)
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSString *result = dict[@"bookName"];
NSLog(@"%@", result);
NSKeyedArchiver歸檔 / NSKeyedUnarchiver解檔:
如果想對(duì)一個(gè)自定義對(duì)象進(jìn)行歸檔解檔,首先要讓對(duì)象遵守NSCoding協(xié)議
其中:NSCoding協(xié)議有2個(gè)方法:
- (void)encodeWithCoder:(NSCoder *)aCoder
歸檔時(shí)調(diào)用這個(gè)方法,在方法中使用encodeObject:forKey:歸檔變量。
- (instancetype)initWithCoder:(NSCoder *)aDecoder
解檔時(shí)調(diào)用這個(gè)方法,在方法中使用decodeObject:forKey讀出變量
NSUserDefaults:
一般使用它來進(jìn)行一些設(shè)置的記錄,比如用戶ID,開關(guān)是否打開等設(shè)置。通過鍵值對(duì)的方式記錄設(shè)置。
NSUserDefaults可以存儲(chǔ)的數(shù)據(jù)類型包括:NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary。如果要存儲(chǔ)其他類型,則需要轉(zhuǎn)換為前面的類型,才能用NSUserDefaults存儲(chǔ)。
FMDB:
FMDB封裝了SQLite的C語言API,更加面向?qū)ο蟆?br>
FMDB中的三個(gè)類:
FMDatabase:可以理解成一個(gè)數(shù)據(jù)庫。
FMResultSet:查詢的結(jié)果集合。
FMDatabaseQueue:運(yùn)用多線程,可執(zhí)行多個(gè)查詢、更新。線程安全。
SQL中的五種數(shù)據(jù)類型:字符型,文本型,數(shù)值型,邏輯型和日期型。
FMDB不支持BOOL類型,所以如果要存的話還是integer類型。
創(chuàng)建數(shù)據(jù)庫:
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.db"];
FMDatabase *db = [FMDatabase databaseWithPath:path];
打開數(shù)據(jù)庫:
if (![db open])
{
? ? db = nil;
? ? return;
}
創(chuàng)建表:
使用集合語句來進(jìn)行表的創(chuàng)建
NSString *sql = @"create table bulktest1 (id integer primary key autoincrement, x text);"
? ? ? ? ? ? ? ? "create table bulktest2 (id integer primary key autoincrement, y text);"
? ? ? ? ? ? ? ? "create table bulktest3 (id integer primary key autoincrement, z text);"
? ? ? ? ? ? ? ? "insert into bulktest1 (x) values ('XXX');"
? ? ? ? ? ? ? ? "insert into bulktest2 (y) values ('YYY');"
? ? ? ? ? ? ? ? "insert into bulktest3 (z) values ('ZZZ');";
success = [db executeStatements:sql];
查詢:
使用executeQuery 來執(zhí)行查詢語句,F(xiàn)MResultSet來進(jìn)行存儲(chǔ)查詢的結(jié)果。
FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
if (oldIDs == nil) oldIDs = [[NSMutableArray alloc] init];
? ? [oldIDs addObject:[s stringForColumnIndex:0]];
}
另外,F(xiàn)MResultSet可以通過如下的方法將數(shù)據(jù)通過恰當(dāng)?shù)母袷綑z索出來
intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dateForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumn:
objectForColumn:
更新:
通過調(diào)用- (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments執(zhí)行插入、刪除或者更新數(shù)據(jù)。
插入數(shù)據(jù)或者更新數(shù)據(jù)。
NSString *sql = ![rs next] ? INSERT_STATEMENT : UPDATE_STATEMENT;
BOOL success = [db executeUpdate:sql withParameterDictionary:[self pr_toDBDictionary]];
if (!success)
{
? ? STLogDBLastError(db);
? ? result = NO;
? ? return;
}
刪除數(shù)據(jù):
BOOL success = [db executeUpdate:@"DELETE FROM STAppLog WHERE userID = ? AND appLogID = ?;", self.userID, self.appLogID];
if (!success)
{
? ? STLogDBLastError(db);
? ? result = NO;
? ? return;
}
多線程操作數(shù)據(jù)庫:
FMDatabaseQueue內(nèi)部實(shí)現(xiàn)了FMDatabase相關(guān)的創(chuàng)建及管理。
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
[queue inDatabase:^(FMDatabase *db) {
? ? [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @1];
? ? [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @2];
? ? [db executeUpdate:@"INSERT INTO myTable VALUES (?)", @3];
? ? FMResultSet *rs = [db executeQuery:@"select * from foo"];
? ? while ([rs next]) {
? ? ? ? …
? ? }
}];
事務(wù):
關(guān)閉數(shù)據(jù)庫:
[db close];
數(shù)據(jù)庫升級(jí)與遷移:
因?yàn)轫?xiàng)目用的是FMDB,所以數(shù)據(jù)遷移就用這個(gè)FMDBMigrationManager工具類,FMDBMigrationManager 是與FMDB結(jié)合使用的一個(gè)第三方,可以記錄數(shù)據(jù)庫版本號(hào)并對(duì)數(shù)據(jù)庫進(jìn)行數(shù)據(jù)庫升級(jí)等操作。
操作步驟:
1.新建一個(gè)Xcode的項(xiàng)目,并集成FMDB和FMDBMigrationManager(建議使用cocoapods,這里不再多說.)
2.創(chuàng)建一個(gè)數(shù)據(jù)庫,這里是我們將要進(jìn)行升級(jí)操作的數(shù)據(jù)庫。
3.將要進(jìn)行升級(jí)操作的數(shù)據(jù)庫與FMDBMigrationManager關(guān)聯(lián)起來
//[self getPath]是數(shù)據(jù)庫路徑
FMDBMigrationManager * manager=[FMDBMigrationManager managerWithDatabaseAtPath:[self getPath] migrationsBundle:[NSBundle mainBundle]];
4.創(chuàng)建遷移表?這個(gè)表就是用來存儲(chǔ)版本號(hào)的
BOOL resultState=NO;
? ? NSError * error=nil;
? ? if (!manager.hasMigrationsTable) {
? ? ? ? resultState=[manager createMigrationsTable:&error];
? ? }
? resultState=[manager migrateDatabaseToVersion:UINT64_MAX progress:nil error:&error];
5.使用自定義類的形式升級(jí),定義一個(gè)新的類:Migration,遵循FMDBMigrating協(xié)議


使用方法也很簡單,將自定義類對(duì)象添加進(jìn)manager即可。
Migration * migration_2=[[Migration alloc]initWithName:@"Book" andVersion:2 andExecuteUpdateArray:@[@"alter table Book add book_email text"]];//給User表添加email字段
?Migration * migration_3=[[Migration alloc]initWithName:@"Book" andVersion:3 andExecuteUpdateArray:@[@"alter table Book add book_address text"]];
? ? [manager addMigration:migration_2];
? ? [manager addMigration:migration_3];
6.加入一個(gè)新增數(shù)據(jù)庫的字段(項(xiàng)目中常用的升級(jí)操作就是增加數(shù)據(jù)庫字段)
- (void)testupdatePrivateMsg{
? ? NSString *filePath = [self getPath];
? ? NSLog(@"-----filePath:%@",filePath);
? ? FMDBMigrationManager * manager=[FMDBMigrationManager managerWithDatabaseAtPath:filePath migrationsBundle:[NSBundle mainBundle]];
? Migration * migration_1=[[Migration alloc]initWithName:@"Book" andVersion:1 andExecuteUpdateArray:@[@"alter table Book add book_email text"]];//給User表添加email字段
? ? Migration * migration_2=[[Migration alloc]initWithName:@"Book" andVersion:2 andExecuteUpdateArray:@[@"alter table Book add book_address text"]];
? ? [manager addMigration:migration_1];
? ? [manager addMigration:migration_2];
? ? BOOL resultState=NO;
? ? NSError * error=nil;
? ? if (!manager.hasMigrationsTable) {
? ? ? ? resultState=[manager createMigrationsTable:&error];
? ? }
? resultState=[manager migrateDatabaseToVersion:UINT64_MAX progress:nil error:&error];
}
FMDB中的坑:
1. 當(dāng)你用字符串去保存數(shù)字時(shí),如果數(shù)字開頭是0的時(shí)候,保存到數(shù)據(jù)庫的話會(huì)把0去掉。這是我往數(shù)據(jù)庫存手勢(shì)密碼發(fā)現(xiàn)的。例如手勢(shì)密碼是03678,存到數(shù)據(jù)庫就成了3678。解決辦法在下面更新數(shù)據(jù)的方法里。同時(shí)存bool類型需要轉(zhuǎn)成NSNumber類型。
- (void)updateData:(XZUserInfomationData *)data
{
? ? if (!data.gesture_code.length) { // 這個(gè)是為了判斷可以忽略
? ? ? data.gesture_code = @"0";
? ? }
? ? [_instance.dbQueue inDatabase:^(FMDatabase *db) {
? ? ? ? NSString *sql = [NSString stringWithFormat:@"UPDATE USERDATA SET net_asset = %@, earned_amount = %@, open_newnet = %d, usable_amount = %@, to_callback_principal = %@, to_callback_earned = %@, open_fingerMark = %d, gesture_code = '%@' WHERE user_name = '%@'", data.net_asset, data.earned_amount, data.open_newnet, data.usable_amount, data.to_callback_principal, data.to_callback_earned, data.open_fingerMark, data.gesture_code, data.user_name];
? ? ? ? if ([db executeUpdate:sql]) {
? ? ? ? ? ? XZLog(@"update success");
? ? ? ? } else {
? ? ? ? ? ? XZLog(@"update failed");
? ? ? ? }
? ? }];
}
更新gesture_code = '%@'加單引號(hào)表明這個(gè)是一個(gè)字符串而不是一串?dāng)?shù)字,SQL會(huì)自己判斷如果不加則會(huì)被認(rèn)為是數(shù)字就自動(dòng)把開頭0去掉。
2、FMDB ?,只能是對(duì)象,不能是基本數(shù)據(jù)類型,如果是int類型,就包裝成NSNumber
3、SQLite3是采用可移植的C(而非Objective-C)編寫的,它不知道什么是NSString,可以用NSString實(shí)例生成C字符串:
const char *stringPath = 【pathString UTF8String】
Core Data
數(shù)據(jù)模型管理類NSManagedObjectModel
通過NSManagedObjectModel,可以將創(chuàng)建的數(shù)據(jù)模型文件讀取為模型管理類對(duì)象。實(shí)體類似于數(shù)據(jù)庫中的表結(jié)構(gòu)。
持久化存儲(chǔ)協(xié)調(diào)者類NSPersistentStoreCoordinator
NSPersistentStoreCoordinator建立數(shù)據(jù)模型與本地文件或數(shù)據(jù)庫之間的聯(lián)系,通過它將本地?cái)?shù)據(jù)讀入內(nèi)存或者將修改過的臨時(shí)數(shù)據(jù)進(jìn)行持久化的保存。
數(shù)據(jù)對(duì)象管理上下文NSManagedObjectContext
NSManagedObjectContext是進(jìn)行數(shù)據(jù)管理的核心類,我們通過這個(gè)類來進(jìn)行數(shù)據(jù)的增刪改查等操作。
如何使用:
創(chuàng)建數(shù)據(jù)庫表:
一般我們需要在工程里創(chuàng)建一個(gè)xxx.xcdatamodeld文件。然后在這個(gè)文件里面創(chuàng)建表
//從本地加載對(duì)象模型
NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"LearnCoreData" ofType:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:modelPath]];
// 創(chuàng)建沙盒中的數(shù)據(jù)庫
NSPersistentStoreCoordinator* coord = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"database.sqlite"];
[coord addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:path] options:nil error:nil];
// 數(shù)據(jù)庫關(guān)聯(lián)緩存
NSManagedObjectContext* objContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
objContext.persistentStoreCoordinator = coord;
查詢數(shù)據(jù):
NSFetchRequest *fetch = [[NSFetchRequest alloc] initWithEntityName:@"User"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"gender=1"]; //查詢條件
fetch.predicate = predicate;
NSArray *results = [objContext executeFetchRequest:fetch error:nil];
for (int i = 0; i < results.count; ++i) {
? ? User *selectedUser = results[i];
? ? NSLog(@"name…:%@", selectedUser.name);
}
更新數(shù)據(jù):
先查詢出來對(duì)象,然后更新對(duì)象響應(yīng)的字段,最后調(diào)用save函數(shù)進(jìn)行保存
// 數(shù)據(jù)插入
User *user = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:objContext];
user.name = [NSString stringWithFormat:@"name_%d", arc4random_uniform(100)];
user.gender = arc4random_uniform(2);
NSError *error;
[objContext save:&error];
刪除對(duì)象:
- (void)dd_delete:(DatabaseCompletion)completion
{
? ? NSManagedObjectContext *localContext = [DatabaseHelper shareInstance].store.privateContext;
? ? [self dd_deleteWithContext:localContext completion:completion];
}
- (void)dd_deleteWithContext:(NSManagedObjectContext *)context completion:(DatabaseCompletion)completion
{
? ? [self MR_deleteEntityInContext:context];
? ? [context save:NULL];
}
數(shù)據(jù)庫升級(jí):
面試題收集:
1.數(shù)據(jù)持久化的幾個(gè)方案(fmdb用沒用過)?
2. 數(shù)據(jù)庫操作FMDB有遇到什么問題嗎?
3.FMDB的升級(jí)與遷移?
4. 什么是序列化和反序列化,用來做什么?
5.?為什么用本地存儲(chǔ),而不用session,session更加簡單啊???
6.數(shù)據(jù)庫的int類型和OC類型int怎么對(duì)應(yīng)存儲(chǔ)?