數(shù)據(jù)存儲(chǔ)

如何存儲(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é)議

.h
.m

使用方法也很簡單,將自定義類對(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ǔ)?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容