iOS常用的數(shù)據(jù)存儲

在iOS開發(fā)過程中,會(huì)經(jīng)常使用到數(shù)據(jù)存儲.數(shù)據(jù)存儲有好幾種常見的方式:plist存儲,偏好設(shè)置,歸檔,數(shù)據(jù)庫等,該篇文章是我想做個(gè)記錄,也想和大家分享下自己的一些見解,涉及的內(nèi)容不是很深,只做實(shí)用性的參考.如果哪位大神對此有深入研究,可以留下來深入討論下,我請吃飯.

沙盒
  • iOS的存儲都是存在沙盒中的,應(yīng)用程序只能訪問自己的沙盒
  • 沙盒目錄里有三個(gè)文件:Documents, Library, tmp
    沙盒目錄圖
沙盒目錄.png
  • Documents:用于存儲用戶數(shù)據(jù)或其它應(yīng)該定期備份的信息
    • 注意,該文件中的內(nèi)容會(huì)在手機(jī)連接電腦時(shí)進(jìn)行同步到云上,所以蘋果不建議開發(fā)者將大量的數(shù)據(jù)寫入到該文件中.如果將視頻圖片等大量的數(shù)據(jù)存入到該文件中,會(huì)被蘋果拒的
// Documents獲取方式
NSString *docStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
  • Library:該目錄下有兩個(gè)子目錄,Caches和Preferences.
    • Caches:一般用于存放應(yīng)用程序?qū)S玫闹С治募?保存應(yīng)用程序再次啟動(dòng)所需要的信息
    • 數(shù)據(jù)存在該文件中
//Caches獲取方法
   NSString *cachesStr = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]
- Preferences:存放應(yīng)用程序的偏好設(shè)置的文件.
  • tmp:用于存放臨時(shí)文件,當(dāng)程序退出時(shí)該文件中的數(shù)據(jù)會(huì)被清空
//tmp獲取方式
 NSString *tmpStr =  NSTemporaryDirectory();
關(guān)于沙盒的詳細(xì)信息,可以去搜下別人的文章,我發(fā)現(xiàn)有很多文章對沙盒有詳細(xì)的講解的,或者參考蘋果官方文檔:About Files and Directories
Plist存儲

定義:plist文件的全名是:Property List解藻,它是一種用來存儲串行化后的對象的文件。文件的擴(kuò)展名是.plist葡盗,通常就稱為plist文件.主要是用來存儲一些OC對象,比如NSArray/NSDictionary,不能存放自定義對象.一般常用的Foundation對象有NSString, NSData, NSDate, NSNumber, NSArray, NSDictionary
存儲原理:寫入沙盒.只要有writeToFile的對象螟左,就能進(jìn)行plist存儲,調(diào)用writeToFile就能自動(dòng)生成plist格式的文件
注意:plist存儲觅够,不能存儲自定義對象胶背,會(huì)失敗的。
plist文件在數(shù)據(jù)更新的時(shí)候需要先取出然后修改在存儲
常用方法:

// 寫入方法
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;
- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically;
// 取出 字典
+ (nullable NSDictionary<KeyType, ObjectType> *)dictionaryWithContentsOfFile:(NSString *)path;
+ (nullable NSDictionary<KeyType, ObjectType> *)dictionaryWithContentsOfURL:(NSURL *)url;
// 取出 數(shù)組
+ (nullable NSMutableArray<ObjectType> *)arrayWithContentsOfFile:(NSString *)path;
+ (nullable NSMutableArray<ObjectType> *)arrayWithContentsOfURL:(NSURL *)url;

獲取沙盒路徑

NSString *str = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

偏好設(shè)置

在開發(fā)中偏好設(shè)置用的還是比較多的,存取比較方便,不需要關(guān)心文件名,其實(shí)底層是利用字典存儲一些鍵值對,你可以存儲后打開路徑文件看一下.
存儲過程是用NSUserDefaults的單利調(diào)用setObject:forKey進(jìn)行存儲.
注意:不能存儲對象,不能及時(shí)存儲,需要做同步操作喘先,把內(nèi)存中的數(shù)據(jù)同步到硬盤上钳吟。

簡單使用

 // 存儲名字jack到沙盒中,存儲的key是 @"name"
    [[NSUserDefaults standardUserDefaults] setObject:@"jack" forKey:@"name"];
    // 同步
    [[NSUserDefaults standardUserDefaults] synchronize];
    // 用key  @"name" 從沙盒中取出名字
    NSString *str =[[NSUserDefaults standardUserDefaults] objectForKey:@"name"];
    

存儲方法:

// 存儲方法(set開頭)
- (void)setObject:(nullable id)value forKey:(NSString *)defaultName;
- (void)setInteger:(NSInteger)value forKey:(NSString *)defaultName;
- (void)setFloat:(float)value forKey:(NSString *)defaultName;
- (void)setDouble:(double)value forKey:(NSString *)defaultName;
- (void)setBool:(BOOL)value forKey:(NSString *)defaultName;
- (void)setURL:(nullable NSURL *)url forKey:(NSString *)defaultName

讀取數(shù)據(jù)的方法:

// 讀取數(shù)據(jù)的方法
- (nullable id)objectForKey:(NSString *)defaultName;
- (nullable NSString *)stringForKey:(NSString *)defaultName;
- (nullable NSArray *)arrayForKey:(NSString *)defaultName;
- (nullable NSDictionary<NSString *, id> *)dictionaryForKey:(NSString *)defaultName;
- (nullable NSData *)dataForKey:(NSString *)defaultName;
- (nullable NSArray<NSString *> *)stringArrayForKey:(NSString *)defaultName;
- (NSInteger)integerForKey:(NSString *)defaultName;
- (float)floatForKey:(NSString *)defaultName;
- (double)doubleForKey:(NSString *)defaultName;
- (BOOL)boolForKey:(NSString *)defaultName;
- (nullable NSURL *)URLForKey:(NSString *)defaultName NS_AVAILABLE(10_6, 4_0);

刪除數(shù)據(jù):

// 刪除數(shù)據(jù)
- (void)removeObjectForKey:(NSString *)defaultName;
歸檔

個(gè)人理解,歸檔的過程就是一個(gè)序列化的過程,然后存儲到指定的路徑路徑文件.歸檔可以存儲對象,需要遵守NSCoding協(xié)議,然后重寫下面兩種方法

- (void)encodeWithCoder:(NSCoder *)aCoder;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder; // NS_DESIGNATED_INITIALIZER
具體使用方法:
  • 自定義模型
Person類的.h文件

#import <UIKit/UIKit.h>

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGFloat height;
@property (nonatomic, assign) NSInteger age;

@end

Person類的.m文件

#import "Person.h"
@interface Person ()<NSCoding>

@end

@implementation Person

// 解檔
// 從一個(gè)給定unarchiver的數(shù)據(jù)中返回一個(gè)初始化對象。
- (instancetype)initWithCoder:(NSCoder *)decoder{
    
    if (self = [super init]) {
        
        self.name = [decoder decodeObjectForKey:@"name"];
        self.age = [decoder decodeIntegerForKey:@"age"];
        self.height = [decoder decodeFloatForKey:@"height"];
        
    }
    return self;
}
// 歸檔
/**
 通過一個(gè)給定的archiver把消息接收者進(jìn)行編碼窘拯。
 當(dāng)接收到encodeObject消息的時(shí)候红且,類終端encodeWithCoder方法被調(diào)用坝茎。
 */
- (void)encodeWithCoder:(NSCoder *)coder{
    
    [coder encodeObject:self.name forKey:@"name"];
    [coder encodeInteger:self.age forKey:@"age"];
    [coder encodeFloat:self.height forKey:@"height"];
    
}
  • 第一種用法
// 創(chuàng)建對象
    Person *person = [Person new];
    person.name = @"jack";
    person.age = 20;
    person.height = 180.0f;

// 獲取路徑
    NSString *str = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"123.archiver"];
// 歸檔
   BOOL flag = [NSKeyedArchiver archiveRootObject:person toFile:filePath];
    if (flag) {
        NSLog(@"歸檔成功");
    }else{
        NSLog(@"歸檔失敗");
    }

// 解檔
   Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:str];
  • 第二種用法
// 創(chuàng)建對象
    Person *person = [Person new];
    person.name = @"jack";
    person.age = 20;
    person.height = 180.0f;

    //  歸檔成二進(jìn)制 (可以用偏好設(shè)置保存或者直接寫入到沙盒)
    NSData *dataArc = [NSKeyedArchiver archivedDataWithRootObject:person];
   [[NSUserDefaults standardUserDefaults] setObject:dataArc forKey:@"archiver"];
   [[NSUserDefaults standardUserDefaults] synchronize];

  // 解檔
    NSData *dataUn = [[NSUserDefaults standardUserDefaults] objectForKey:@"archiver"];
    Person *unPerson = [NSKeyedUnarchiver unarchiveObjectWithData:dataUn];
    
  • 第三種用法
    // 獲取路徑
    NSString *str = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"123.archiver"];
    // 創(chuàng)建一個(gè)二進(jìn)制對象
    NSMutableData *data = [[NSMutableData alloc] init];
    // 設(shè)置歸檔寫入對象
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    // 歸檔
    [archiver encodeObject:person forKey:@"archiver"];// archivingDate的encodeWithCoder方法被調(diào)用
    [archiver finishEncoding];
    // 保存二進(jìn)制到沙盒中
    [data writeToFile:str atomically:YES];

    // 解檔
    // 通過文件獲取一個(gè)二進(jìn)制對象
    NSMutableData *data = [[NSMutableData alloc] initWithContentsOfFile:str];
    // 讀取二進(jìn)制文件
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    // 獲得類
    Person *person = [unarchiver decodeObjectForKey:@"person"]; ;// initWithCoder方法被調(diào)用
    [unarchiver finishDecoding];
    

在自定義模型的.m文件中,重寫 - (instancetype)initWithCoder:(NSCoder *)decoder 時(shí),調(diào)用的是[super init], 而不是 [super initWithCoder:decoder]; 這是因?yàn)镻erson類繼承的是NSObject,NSObject類沒有遵守NSCoding這個(gè)協(xié)議.
initWithCoder原理:只要解析文件就會(huì)調(diào)用,xib,storyboard都是文件暇番,因此只要解析這兩個(gè)文件嗤放,就會(huì)調(diào)用initWithCoder。
因此如果在storyboard使用自定義view,重寫initWithCoder方法壁酬,一定要調(diào)用[super initWithCoder:]次酌,因?yàn)橹挥邢到y(tǒng)才知道怎么解析storyboard,如果沒有調(diào)用舆乔,就解析不了這個(gè)文件岳服。
重寫NSCoding協(xié)議的方法時(shí)有一個(gè)不便的地方就是,當(dāng)自定義模型特別多的時(shí)候,每個(gè).m文件中都需要重寫一遍,挺麻煩的.下面這個(gè)文件是通過運(yùn)行時(shí)的方法遍歷模型中的屬性進(jìn)行設(shè)置的,在使用的時(shí)候,需要導(dǎo)入#import "GGArchiverCoding.h", #import <objc/runtime.h>頭文件,在.m文件中寫入這句話就可以了:NSCodingImplementation;

GGArchiverCoding.h 文件
注意:該文件可以原版copy的

#define NSCodingImplementation \
- (instancetype)initWithCoder:(NSCoder *)aDecoder{\
    if (self = [super init]) { \
        unsigned int count; \
        Ivar *ivarList = class_copyIvarList([self class], &count);\
        for (int i = 0; i < count; i++) {\
            Ivar ivar = ivarList[i];\
            const char *ivarName = ivar_getName(ivar);\
            NSString *key = [NSString stringWithUTF8String:ivarName];\
            id value = [aDecoder decodeObjectForKey:key]; \
            [self setValue:value forKey:key];\
        }\
    }\
    return self;\
}\
- (void)encodeWithCoder:(NSCoder *)aCoder{ \
    unsigned int count;\
    Ivar *ivarList = class_copyIvarList([self class], &count);\
    for (int i = 0; i < count; i++) {\
        Ivar ivar = ivarList[i];\
        const char *ivarName = ivar_getName(ivar);\
        NSString *key = [NSString stringWithUTF8String:ivarName];\
        id value = [self valueForKeyPath:key];\
        [aCoder encodeObject:value forKey:key];\
    }\
}

iOS中最常見的三種存儲方式介紹完了,雖然使用率還是蠻好的,對于大型數(shù)據(jù)的存儲上面三種就做不到了,或者說想做的很麻煩的.數(shù)據(jù)庫就能這解決這種問題

SQLite介紹(PS:下面只是對SQLite3的簡單實(shí)用的介紹,沒有做詳細(xì)的講解,事后詳情再補(bǔ)上.如果哪位大神有詳細(xì)介紹的文章,可以跟帖鏈接,共同學(xué)習(xí))

實(shí)際開發(fā)過程中,很少會(huì)直接用SQLite3,常常會(huì)采用一些對SQLite3封裝好的三方框架,FMDB就是一個(gè)非常優(yōu)秀的框架.
(度娘介紹):SQLite是一款輕型的嵌入式數(shù)據(jù)庫,它占用資源非常的低,在嵌入式設(shè)備中希俩,可能只需要幾百K的內(nèi)存就夠了,它的處理速度比Mysql派阱、PostgreSQL這兩款著名的數(shù)據(jù)庫都還快,數(shù)據(jù)庫(Database)是按照數(shù)據(jù)結(jié)構(gòu)來組織、存儲和管理數(shù)據(jù)的倉庫.
想學(xué)習(xí)數(shù)據(jù)就,建議安裝下Navicat,用著很不錯(cuò)的一款數(shù)據(jù)庫軟件.
數(shù)據(jù)庫可以分為兩大種類:關(guān)系型數(shù)據(jù)庫(主流)和 對象型數(shù)據(jù)庫
常用關(guān)系型數(shù)據(jù)庫PC端:Oracle斜纪、MySQL、SQL Server文兑、Access盒刚、DB2、Sybase.嵌入式\移動(dòng)客戶端:SQLite

數(shù)據(jù)庫的存儲結(jié)構(gòu)和excel很像绿贞,以表(table)為單位
數(shù)據(jù)庫的每一列叫字段(column因块,列,屬性)
每一行叫記錄(row籍铁,record涡上,每行存放多個(gè)字段對應(yīng)的值)
建表:Tabel Name: 填寫表明,建議以_t開頭

常用的關(guān)鍵字有:select、insert拒名、update吩愧、delete、from增显、create雁佳、where、desc同云、order糖权、by、group炸站、table星澳、alter、view旱易、index等等數(shù)據(jù)庫中不可以使用關(guān)鍵字來命名表禁偎、字段

  • SQL語句種類:
    • DDL:數(shù)據(jù)定義語句:包括create(創(chuàng)表)等操作
    • DML:數(shù)據(jù)操作語句:包括:insert(增加)腿堤、update(修改)、delete(刪除)等操作
    • DQL:數(shù)據(jù)查詢語句:最常用的關(guān)鍵字select(查詢),別的常用的關(guān)鍵字有where届垫,order by释液,group by和having
  • 字段類型:SQL將數(shù)據(jù)劃分為四種數(shù)據(jù)類型
    • integer : 整形值
    • real:浮點(diǎn)值
    • text:文本字符串
    • blob:二進(jìn)制數(shù)據(jù)
  • 主鍵:用來唯一標(biāo)識某一條記錄,在創(chuàng)表的時(shí)候用primary key聲明一個(gè)主鍵

創(chuàng)表

// 在編寫數(shù)據(jù)庫語句是,盡可能多的添加一些約束條件,這樣可以方便交流

// 導(dǎo)入數(shù)據(jù)庫頭文件
#import <sqlite3.h>

// 保存數(shù)據(jù)庫實(shí)例對象
@property (nonatomic, assign) sqlite3 *db;


// 獲取沙盒路徑
    NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"123.db"];
 // 將OC字符串 轉(zhuǎn)成 C語言字符串
    const char *cFilePath = filePath.UTF8String;
// 創(chuàng)表
    // 將根據(jù)文件路徑打開數(shù)據(jù)庫,如果不存在装处,則會(huì)創(chuàng)建一個(gè)新的數(shù)據(jù)庫
    // 返回值表示狀態(tài)
    int result = sqlite3_open(cFilePath, &_db);
  if (result == SQLITE_OK) {
        NSLog(@"成功打開數(shù)據(jù)庫");
    // 創(chuàng)表
        const char *sql = "CREATE TABLE IF NOT EXISTS t_shop (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, price integer NOT NULL);";
        char *erroMsg = NULL;
        // 創(chuàng)表語句  (exec:執(zhí)行sql語句)
       sqlite3_exec(_db, sql, NULL, NULL, &erroMsg);
        if (erroMsg) {
            NSLog(@"創(chuàng)表失敗--%s", erroMsg);
        }
    }else{
         NSLog(@"打開數(shù)據(jù)庫失敗");
    }
  // 移動(dòng)端在操作數(shù)據(jù)庫的時(shí)候一般不會(huì)考慮關(guān)閉
  // 關(guān)閉數(shù)據(jù)庫:sqlite3_close(db);

增加數(shù)據(jù)

    static int i = 0;
    i++;
    NSString *name = [NSString stringWithFormat:@"name-%d", i];
    
    NSString *sql = [NSString stringWithFormat:@"INSERT INTO t_shop (name, price) VALUES ('%@', %d);", name, i];
    char *errMsg;
    sqlite3_exec(self.db, sql.UTF8String, NULL, NULL, &errMsg);
    NSLog(@"錯(cuò)誤信息 = %s", errMsg);

刪除數(shù)據(jù)

// 刪除price大于10的數(shù)據(jù)
    NSString *sql = [NSString stringWithFormat:@"DELETE FROM t_shop where price > 10"];
   char *errMsg;
   sqlite3_exec(self.db, sql.UTF8String, NULL, NULL, NULL);
    NSLog(@"錯(cuò)誤信息 = %s", errMsg);

修改數(shù)據(jù)

// price小于5,設(shè)置為0
    NSString *sql = [NSString stringWithFormat:@"UPDATE t_shop set price = 0 where price < 5"];
    char *errMsg;
    sqlite3_exec(self.db, sql.UTF8String, NULL, NULL, &errMsg);
    NSLog(@"%s", errMsg);

查詢數(shù)據(jù)

 // 查詢數(shù)據(jù)如果用  sqlite3_exec(適合執(zhí)行沒有返回?cái)?shù)據(jù)的操作)  需要用到第三個(gè)參數(shù),函數(shù)的回調(diào)


    // 查詢所有字段
    const char *sql = "SELECT * FROM t_shop;";
    // 準(zhǔn)備
    //  第三個(gè)是sql長度, -1自動(dòng)計(jì)算
    // stmt是用來取出查詢結(jié)果的
    sqlite3_stmt *stmt = NULL;
    int status = sqlite3_prepare_v2(self.db, sql, -1, &stmt, NULL);
    if (status == SQLITE_OK) { // 準(zhǔn)備成功 -- SQL語句正確
        
        // sqlite3_step  步驟(一步一步的執(zhí)行)
        while (sqlite3_step(stmt) == SQLITE_ROW) { // 成功取出一條數(shù)據(jù)
            // 后面的數(shù)字是列號
            // 列好如果為0,代表的是主鍵的那列
            const char *name = (const char *)sqlite3_column_text(stmt, 1);
            const char *price = (const char *)sqlite3_column_text(stmt, 2);
            
            NSLog(@"name = %s, price = %s", name, price);
        }
    }
    
FMDB簡單用法

FMDB是以0C的方式封裝了SQLite的C語言,用起來更加的面相對象,但是SQL語句還是需要寫的.這比起單純的使用SQLite3去做數(shù)據(jù)存儲已經(jīng)方便了很多,而且FMDB還對數(shù)據(jù)的存取操作做了線程安全的保證.
下載下來后的FMDB目錄結(jié)構(gòu)

FMDB.png

在實(shí)際操作過程中,主要用到三個(gè)類
FMDatabase:一個(gè)FMDatabase對象就代表一個(gè)單獨(dú)的SQLite數(shù)據(jù)庫
FMResultSet:查詢后的結(jié)果集.
FMDatabaseQueue:用于在多個(gè)線程中執(zhí)行多個(gè)查詢或更新,保證線程安全的.

  • 簡單用法
    創(chuàng)建FMDatabase對象(打開數(shù)據(jù)庫)
// FMDB提供了兩個(gè)方法去創(chuàng)建FMDatabase對象
// 通過指定數(shù)據(jù)庫文件路徑來創(chuàng)建FMDatabase對象
+ (instancetype)databaseWithPath:(NSString*)inPath;
- (instancetype)initWithPath:(NSString*)inPath;
// path文件路徑
FMDatabase *db = [FMDatabase databaseWithPath:path];
if (![db open]) {
    NSLog(@"數(shù)據(jù)庫打開失斘笳!");
}

// 關(guān)閉數(shù)據(jù)庫
// [db close];

文件路徑有三種情況(在FMDB文檔里有說明的)
1,具體的系統(tǒng)文件路徑,如果不存FMDatabase對象在會(huì)自動(dòng)創(chuàng)建
2,數(shù)據(jù)庫路徑為空字符串@"", 會(huì)在零食目錄創(chuàng)建一個(gè)空的數(shù)據(jù)庫,當(dāng)FMDatabase關(guān)閉連接時(shí),數(shù)據(jù)庫文件也會(huì)被刪除
3,nil,會(huì)創(chuàng)建一個(gè)內(nèi)存中臨時(shí)數(shù)據(jù)庫,當(dāng)FMDatabase關(guān)閉連接時(shí),  數(shù)據(jù)庫也會(huì)被銷毀.

執(zhí)行更新

在FMDB中妄迁,除查詢以外的所有操作寝蹈,都稱為“更新”create、drop登淘、insert箫老、update、delete等
// 下面這些方法在FMDatabase.h文件有詳細(xì)的說明,建議查看頭文件.
使用executeUpdate:方法執(zhí)行更新,sql和format是數(shù)據(jù)庫字符串
- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
// 示例
// 創(chuàng)表
NSString *creatTableSql = @"CREATE TABLE IF NOT EXISTS t_userTable (id integer PRIMARY KEY autoincrement,  name text, price integer)";
[db executeUpdate:creatTableSql];
// 增加數(shù)據(jù)
[db executeUpdate:@"UPDATE t_shop SET price = ? WHERE name = ?;", @20, @"Jack"] 

執(zhí)行查詢

查詢方法在FMDatabase.h文件中,返回的是FMResultSet結(jié)果集
常用的查詢方法,sql和format是數(shù)據(jù)庫字符串
- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
// 示例
// 查詢數(shù)據(jù)
FMResultSet *rs = [db executeQuery:@"SELECT * FROM t_shop"];
// 遍歷結(jié)果集
while ([rs next]) {
    NSString *name = [rs stringForColumn:@"name"];
    int price = [rs intForColumn:@"price"];
} 

// FMDatabaseQueue提供了三個(gè)對象方法
//1. 同步隊(duì)列執(zhí)行數(shù)據(jù)庫操作黔州。
- (void)inDatabase:(void (^)(FMDatabase *db))block;
//2.使用事務(wù)進(jìn)行同步隊(duì)列執(zhí)行數(shù)據(jù)庫操作
- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;
//3.使用遞延交易
- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;

在項(xiàng)目中用FMDB做數(shù)據(jù)的存取的時(shí)候需要考慮到線程的安全問題,所以我在使用的時(shí)候是這樣用的


//  數(shù)據(jù)庫隊(duì)列
static FMDatabaseQueue *queue = nil;

    if (!queue) {
        NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"fmdb.db"];
        // 該方法會(huì)根據(jù)路徑去獲取一個(gè)隊(duì)列,如果路徑不存在會(huì)自動(dòng)創(chuàng)建一個(gè)數(shù)據(jù)庫
        queue = [FMDatabaseQueue databaseQueueWithPath:path];
    }
    
    if (queue == nil) {
        NSLog(@"創(chuàng)建隊(duì)列失敗");
        return ;
    }

   // 創(chuàng)表
    [queue inDatabase:^(FMDatabase *db) {
        NSString *creatTableSql = @"CREATE TABLE IF NOT EXISTS t_userTable (id integer PRIMARY KEY autoincrement, uid integer, name text, timeStamp integer)";
       [db executeUpdate:creatTableSql];
    }];

// 添加數(shù)據(jù)
    static int i = 0;
    time_t now;
    time_t timeStamp = time(&now);
    i++;
    [queue inDatabase:^(FMDatabase *db) {
        [db executeUpdateWithFormat:@"INSERT INTO t_userTable (uid, name, timeStamp) VALUES (%d, %@, %ld)", i, @"jack", timeStamp];
    }];
    
// 查詢數(shù)據(jù)
    [queue inDatabase:^(FMDatabase *db) {
        NSString *query = [NSString stringWithFormat:@"SELECT * FROM t_userTable"];
        FMResultSet *set = [db executeQuery:query];
        while ([set next]) {
            NSString *uid = [set stringForColumn:@"uid"];
            NSString *name = [set stringForColumn:@"name"];
            long int timeStamp = [set intForColumn:@"timeStamp"];
        }
    }];

// 修改數(shù)據(jù)
    NSString *update = [NSString stringWithFormat:@"UPDATE t_userTable set uid = 100 where uid > 10"];
    [queue inDatabase:^(FMDatabase *db) {
        [db executeUpdate:update];
    }];

// 刪除數(shù)據(jù)
    NSString *delete = [NSString stringWithFormat:@"DELETE FROM t_userTable where uid < %zd", 10];
    [queue inDatabase:^(FMDatabase *db) {
        [db executeUpdate:delete];
    }];

關(guān)于數(shù)據(jù)庫的詳細(xì)的知識,后期不忙了再補(bǔ)上.大神們?nèi)缬性敿?xì)見解,期望您

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耍鬓,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子流妻,更是在濱河造成了極大的恐慌牲蜀,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绅这,死亡現(xiàn)場離奇詭異涣达,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)证薇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門度苔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人浑度,你說我怎么就攤上這事寇窑。” “怎么了箩张?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵疗认,是天一觀的道長。 經(jīng)常有香客問我伏钠,道長横漏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任熟掂,我火速辦了婚禮缎浇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赴肚。我一直安慰自己素跺,他們只是感情好二蓝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著指厌,像睡著了一般刊愚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上踩验,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天鸥诽,我揣著相機(jī)與錄音,去河邊找鬼箕憾。 笑死牡借,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的袭异。 我是一名探鬼主播钠龙,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼御铃!你這毒婦竟也來了碴里?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤上真,失蹤者是張志新(化名)和其女友劉穎咬腋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谷羞,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年溜徙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了湃缎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蠢壹,死狀恐怖嗓违,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情图贸,我是刑警寧澤蹂季,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站疏日,受9級特大地震影響偿洁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沟优,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一涕滋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挠阁,春花似錦宾肺、人聲如沸溯饵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丰刊。三九已至,卻和暖如春增拥,著一層夾襖步出監(jiān)牢的瞬間啄巧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工跪者, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棵帽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓渣玲,卻偏偏與公主長得像逗概,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子忘衍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容