1. 程式人生 > >iOS 資料儲存的幾種方式

iOS 資料儲存的幾種方式

在iOS開發過程中常用的本地化儲存有五種方式:

1.plist (XML屬性列表歸檔 NSArray\NSDictionary)

2.preference (偏好設定\NSUserDefaults) (本質還是通過plist來儲存資料,但是使用更加簡單,無需關注檔案、資料夾路徑和名稱)

3.NSCoding (NSKeyedArchiver\NSKeyedUnarchiver)  (能把任何物件都直接儲存成檔案的方式)

4.SQLite3  (當非常大量的資料時候才會使用)

5.Core Data (是對SQLite3的封裝,更加面向物件,效率沒有SQLite3高)

沙盒(sandbox):每個iOS應用都有自己的應用沙盒(應用沙盒就是應用的資料夾),與其他檔案系統隔離。應用必須待在自己的沙盒裡,其他應用不能訪問該沙盒
應用沙盒的檔案系統目錄,如下圖所示(假設應用的名稱叫Layer)

沙盒結構分析:
應用程式包:(上圖中的Layer)包含了所有的資原始檔和可執行檔案
Documents:儲存應用執行時生成的需要持久化的資料,iTunes同步裝置時會備份該目錄。例如,遊戲應用可將遊戲存檔儲存在該目錄。儲存相對重要的資料。

tmp:儲存應用執行時所需的臨時資料,使用完畢後再將相應的檔案從該目錄刪除。應用沒有執行時,系統也可能會清除該目錄下的檔案。iTunes同步裝置時不會備份該目錄。儲存不重要的並且大的資料。

Library/Caches:儲存應用執行時生成的需要持久化的資料,iTunes同步裝置時不會備份該目錄。一般儲存體積大、不需要備份的非重要資料

Library/Preference:

儲存應用的所有偏好設定,iOS的Settings(設定)應用會在該目錄中查詢應用的設定資訊。iTunes同步裝置時會備份該目錄。該目錄由系統管理, 無需我們來管理。通常用來儲存一些基本的軟體配置資訊, 比如記住密碼、自動登入等。

總結: 我們平時操作資料主要使用Documents目錄。

沙盒的目錄的獲取方式
沙盒根目錄的獲取:
NSString *home = NSHomeDirectory();
1.利用沙盒根目錄拼接”Documents“字串:
//這種方式不建議使用,因為如果新版本的作業系統可能會修改目錄的名稱
NSString *home = NSHomeDirectory();
NSString *documents = [home stringByAppendingPathComponent:@“Documents”];

2.利用NSSearchPathForDirectoriesInDomains函式

// NSUserDomainMask 代表從使用者資料夾下找
// YES 代表展開路徑中的波浪字元“~”
NSArray *array =  NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// 在iOS中,只有一個目錄跟傳入的引數匹配,所以這個集合裡面只有一個元素
NSString *documents = [array objectAtIndex:0];

常見沙盒目錄獲取方式:
tmp:NSString *tmp = NSTemporaryDirectory();
Library/Caches:(跟Documents類似的2種方法)
利用沙盒根目錄拼接”Caches”字串
利用NSSearchPathForDirectoriesInDomains函式(將函式的第2個引數改為:NSCachesDirectory即可)
Library/Preference:通過NSUserDefaults類存取該目錄下的設定資訊

1、XML屬性列表(plist):(不能儲存基本資料型別只能儲存OC物件)

屬性列表是一種 xml格式的檔案,拓展名為plist,如果物件是(必須是OC物件,不能儲存基本資料型別)NSString、NSDictionary/NSArray、NSData 、NSNumber等型別,就可以使用writeToFile:atomically:方法直接將物件寫到屬性列表檔案中,然後用dictionaryWithContentsOfFile讀取資料。

示例程式碼如下:

//儲存
- (IBAction)save {
        
        //1.獲取沙盒根路徑
        NSString *home = NSHomeDirectory();
        //2.document路徑
        NSString *docPath = [home stringByAppendingPathComponent:@"Documents"];
        //3.新建資料  資料一般只能儲存oc物件  字典也可以
        NSArray *data = @[@"jack",@"mack",@15];
        //4.寫資料
        NSString *filePath = [docPath stringByAppendingPathComponent:@"data.plist"];
        [data writeToFile:filePath atomically:YES];
}
//讀取
- (IBAction)read {
        
        //1.獲取沙盒根路徑
        NSString *home = NSHomeDirectory();
        //2.document路徑
        NSString *docPath = [home stringByAppendingPathComponent:@"Documents"];
        //3.檔案路徑
        NSString *filePath = [docPath stringByAppendingPathComponent:@"data.plist"];
        //4.讀取資料
        NSArray *data = [NSArray arrayWithContentsOfFile:filePath];
        NSLog(@"%@",data);
}

//將一個NSDictionary物件歸檔到一個plist屬性列表中
// 將資料封裝成字典
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:@"母雞" forKey:@"name"];
[dict setObject:@"15013141314" forKey:@"phone"];
[dict setObject:@"27" forKey:@"age"];
// 將字典持久化到Documents/stu.plist檔案中
[dict writeToFile:path atomically:YES];

//讀取屬性列表,恢復NSDictionary物件
// 讀取Documents/stu.plist的內容,例項化NSDictionary
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(@"name:%@", [dict objectForKey:@"name"]);
NSLog(@"phone:%@", [dict objectForKey:@"phone"]);
NSLog(@"age:%@", [dict objectForKey:@"age"]);

2、prefrrence (偏好設定)

很多iOS應用都支援偏好設定,比如儲存使用者名稱、密碼、字型大小等設定,iOS提供了一套標準的解決方案來為應用加入偏好設定功能
每個應用都有個NSUserDefaults例項,通過它來存取偏好設定,比如,儲存使用者名稱、字型大小、是否自動登入等。

//儲存資料
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"itcast" forKey:@"username"];
[defaults setFloat:18.0f forKey:@"text_size"];
[defaults setBool:YES forKey:@“auto_login"];
//讀取上次儲存的設定
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *username = [defaults stringForKey:@"username"];
float textSize = [defaults floatForKey:@"text_size"];
BOOL autoLogin = [defaults boolForKey:@"auto_login"];
[defaults synchornize];(立刻同步)
注意:UserDefaults設定資料時,不是立即寫入,而是根據時間戳定時地把快取中的資料寫入本地磁碟。所以呼叫了set方法之後資料有可能還沒有寫入磁碟應用程式就終止了。出現以上問題,可以通過呼叫synchornize方法強制寫入

示例程式碼如下:

//偏好設定資料儲存:(一般用來儲存軟體的配置,儲存在Library/Preferences中,不需要關注資料夾的路徑)

//儲存
- (IBAction)save {
           
        //1.利用NSUserDefaults,就能直接訪問軟體的偏好設定(Library/Preferences
       NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        //2.儲存資料
        [defaults setObject:@"jack" forKey:@"name"];
        [defaults setInteger:10 forKey:@"age"];
        [defaults setBool:YES forKey:@"Login"];
        //3.立刻同步
        [defaults synchronize];
}

//讀取
- (IBAction)read {
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        NSString *name = [defaults objectForKey:@"name"];
        BOOL login = [defaults objectForKey:@"Login"];
        NSLog(@"%@-- %d",name,login);
}

3、NSCoding (NSKeyedArchiver\NSKeyedUnarchiver)

如果物件是NSString、NSDictionary、NSArray、NSData、NSNumber等型別,可以直接用NSKeyedArchiver進行歸檔和恢復
不是所有的物件(非OC物件)都可以直接用這種方法進行歸檔只有遵守了NSCoding協議的物件才可以
NSCoding協議有2個方法:
encodeWithCoder:每次歸檔物件時,都會呼叫這個方法。一般在這個方法裡面指定如何歸檔物件中的每個例項變數,可以使用encodeObject:forKey:方法歸檔例項變數
initWithCoder:每次從檔案中恢復(解碼)物件時,都會呼叫這個方法。一般在這個方法裡面指定如何解碼檔案中的資料為物件的例項變數,可以使用decodeObject:forKey方法解碼例項變數


示例程式碼如下:

NSKeyedArchiver資料儲存,不僅可以用來儲存OC物件,也可以用來儲存非OC物件,以一個Person物件為例:(想歸檔某個物件,這個物件一定要遵守NSCoding這個協議)
#import <Foundation/Foundation.h>
@interface TWPerson : NSObject <NSCoding>
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) int age;
@property (nonatomic,assign) double height;
@end

@implementation TWPerson
//將物件歸檔的時候呼叫(將物件寫入檔案之前呼叫)
//在這個方法裡說清楚:1.哪些屬性需要儲存  2.怎樣儲存這些屬性
-(void)encodeWithCoder:(NSCoder *)enCoder
{
        //將_name屬性進行編碼(會將_name的值存進檔案)
        [enCoder encodeObject:_name forKey:@"name"];
        [enCoder encodeInt:_age forKey:@"age"];
        [enCoder encodeDouble:_height forKey:@"height"];
    
}
//當從檔案中解析物件時使用
//在這個方法說清楚: 1.那個屬性需要解析(讀取)  2.怎樣解析(讀取)這些屬性
- (id)initWithCoder:(NSCoder *)Decoder
{
    
        if (self = [super init]) {
                _name = [Decoder decodeObjectForKey:@"name"];
                _age = [Decoder decodeIntForKey:@"age"];
                _height = [Decoder decodeDoubleForKey:@"height"];
            }
        return self;
}
@end

//儲存資料
- (IBAction)save {
        TWPerson *p = [[TWPerson alloc]init];
        p.name = @"jack";
        p.age = 10;
        p.height = 1.82;
        NSString *path = @"/Users/wtw/Desktop/person.data";
        //歸檔
        [NSKeyedArchiver archiveRootObject:p toFile:path];
}
//讀取資料
- (IBAction)read {
        NSString *path = @"/Users/wtw/Desktop/person.data";
        //讀檔(反歸檔)
       TWPerson *p = [NSKeyedUnarchiver  unarchiveObjectWithFile:path];
        NSLog(@"%@ -- %f -- %d",p.name,p.height,p.age);
}
4、SQLite3
SQLite3 是一款開源的輕型的嵌入式關係型資料庫,可移植性好、易使用、記憶體開銷小,SQLite3 是無型別的,可以儲存任何型別的資料到任意的欄位中。

資料庫儲存資料的步驟:

》新建一個數據庫(DataBase)

》新建一張表(table)

》新增多個欄位(column,列,屬性)

》新增多行記錄(row,每行存放多個欄位對應的值)

SQL語句的分類:

資料定義語句:(DDL :Data Definition Language)

包括create drop 等操作。

在資料庫中建立新表或者刪除表(creat table 或 drop table)

資料庫操作語句: (DML : Data manipulation Language)

包括 insert 、update 、delete 等操作。

上面的3種操作分別用於新增、修改、刪除表中的資料

資料查詢語句 :(DQL : Data Query Language)

可以用於查詢獲得表中的資料

關鍵字select 是 DQL 用的最多的操作

其他DQL 常用的關鍵字有 where 、order by、group by 和 having 等。

建立表:

//建立表:
/*
 建立資料表
 CREATE TABLE '表名' (
 '欄位1' 型別(INTEGER, REAL, TEXT, BLOB)
    NOT NULL    不允許為空
    PRIMARY KEY    主鍵
    AUTOINCREMENT 自增長,
 '欄位名2' 型別,
 ...
 )
*/
提示:可以從navicat先建立好表,複製貼上,需要自己增加 IF NOT EXISTS

CREATE TABLE IF NOT EXISTS "T_Person" (
       "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
       "name" TEXT,
       "age" INTEGER,
       "heigth" REAL
       )
刪除表:
//刪除表
//DROP TABLE t_student;
DROP TABLE IF EXISTS t_student;
插入資料:
//插入命令:
INSERT INTO 表名
    (欄位名1,欄位名2…..)
    VALUES
    (值1,值2。。。。)
//注意:值和欄位一定要一致
//提示:SQLite的欄位屬性,只是給程式設計師看的,存什麼都行

INSERT INTO t_student
    (age, score, name)
    VALUES
    ('28', 100, 'zhangsan');
更新資料
//更新資料:
UPDATE  表名
    SET
    欄位1 = ‘值1’  ,        //提示: 如果是字串,需要使用單引號引起來
    欄位2 = 值2 ,...
/*更新記錄的name*/
UPDATE t_student SET name = 'zhangsan';
//注意:如果更新時沒有指定更新哪一條記錄,那麼會更新所有的資料

刪除資料
//刪除指令:
//注意:刪除資料的時候,一定要設定條件
//如果條件不滿足,什麼也不會發生

DELETE  FROM  表名
    WHERE 主鍵 = 值;

//例如:
DELETE FROM t_student;
DELETE FROM t_student WHERE age < 50;

查詢資料
//查詢指令:
/* 分頁 */
SELECT * FROM t_student
ORDER BY id ASC LIMIT 30, 10;
/* 排序 */
SELECT * FROM t_student
WHERE score > 50
ORDER BY age DESC;
SELECT * FROM t_student
WHERE score < 50
ORDER BY age ASC , score DESC;
/* 計量 */
SELECT COUNT(*)
FROM t_student
WHERE age > 50;
/* 別名 */
SELECT name as myName, age as myAge, score as myScore
FROM t_student;
SELECT name myName, age myAge, score myScore
FROM t_student;
SELECT s.name myName, s.age myAge, s.score myScore
FROM t_student s
WHERE s.age > 50;
/* 查詢 */
SELECT name, age, score FROM t_student;
SELECT * FROM t_student;

資料庫的基本使用:

     @property (nonatomic, assign) sqlite3 *db;  
     /*
     1.建立資料庫
     2.建立表(指定欄位)
     3.操作資料庫
     */
    /*
     第一個引數:是告訴系統資料庫檔案在什麼地方.
     >如果檔案不存在就會自動建立, 然後再開啟
     >如果資料庫存在就會自動開啟
     第二個引數:返回開啟的資料庫物件
     */
    // 1.拼接資料庫地址
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
    NSString *sqlFile = [path stringByAppendingPathComponent:@"student.sqlite"];
//    sqlite3 *db = NULL;
    // 2.開啟資料庫
    int result = sqlite3_open(sqlFile.UTF8String, &_db);// [self db]
    // 3.判斷是否開啟成功
    if (result == SQLITE_OK) {
        NSLog(@"開啟成功");
        // 3.1建立表
        /*
         第一個引數: 需要執行SQL語句的資料庫物件
         第二個引數: 需要執行的SQL語句
         第三個引數: 回撥函式
         第四個引數: 第三個引數的引數
         第五個引數: 接收錯誤資訊
         */
        NSString *sql = @"CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT , name TEXT, age INTEGER, score REAL);";
        result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, NULL);
        if (result == SQLITE_OK) {
            NSLog(@"建立表成功");
        }else
        {
            NSLog(@"建立表失敗");
        }
    }else
    {
        NSLog(@"開啟失敗");
    }
- (IBAction)insertClick:(id)sender {
    
    NSString *sql = @"INSERT INTO t_student(age, score, name) VALUES ('28', 100, 'jonathan');";
    int result =  sqlite3_exec(_db, sql.UTF8String, NULL, NULL, NULL);
    if (result == SQLITE_OK) {
        NSLog(@"插入成功");
    }
}

- (IBAction)updateClick:(id)sender {
    NSString *sql = @"UPDATE t_student SET name = 'XXX';";
    int result =  sqlite3_exec(_db, sql.UTF8String, NULL, NULL, NULL);
    if (result == SQLITE_OK) {
        NSLog(@"修改成功");
    }
}

- (IBAction)deleteClick:(id)sender {
    NSString *sql = @"DELETE FROM t_student WHERE id = 1; ";
    int result =  sqlite3_exec(_db, sql.UTF8String, NULL, NULL, NULL);
    if (result == SQLITE_OK) {
        NSLog(@"刪除成功");
    }6
}

- (IBAction)selectClick:(id)sender {
    /*
     sqlite3操作中, 所有DML語句都是使用sqlite3_exec函式執行SQL語句即可,
     但是如果是需要查詢資料庫, 不能使用sqlite3_exec, 因為它並沒有返回查詢到得結果發給我們
     */
    /*
    NSString *sql = @"SELECT * FROM t_student;";
    int result =  sqlite3_exec(_db, sql.UTF8String, NULL, NULL, NULL);
    if (result == SQLITE_OK) {
        NSLog(@"查詢成功");
    }
     */
    // 1.準備查詢
    /*
     第一個引數:需要執行SQL語句的資料庫
     第二個引數:需要執行的SQL語句
     第三個引數: 告訴系統SQL語句的長度, 如果傳入一個小於0的數, 系統會自動計算
     第四個引數:結果集, 裡面存放所有查詢到的資料(不嚴謹)
     */
    NSString *sql = @"SELECT * FROM t_student;";
    sqlite3_stmt *stemt = NULL;
    sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stemt, NULL);
    // 2.判斷有沒有查詢結果
//    int result = sqlite3_step(stemt);
//    if (result == SQLITE_ROW) {
    while (sqlite3_step(stemt) == SQLITE_ROW) {
//        NSLog(@"查詢到了資料");
        // 3.取出查詢到得結果
        const unsigned char *name = sqlite3_column_text(stemt, 1);
        int age = sqlite3_column_int(stemt, 2);
        double score = sqlite3_column_double(stemt, 3);
        NSLog(@"%s %d %f", name, age, score);
    }
}

資料庫的基本的封裝
@interface SQLManager : NSObject

+ (instancetype)shareManage;
//插入新資料
- (BOOL)insertStudent:(HMStudent *)student;
//刪除資料
- (BOOL)deleteWithStudent:(HMStudent *)student;
//更新資料
- (BOOL)updateWithStudent:(HMStudent *)student;
//查詢資料
- (NSArray *)query;
@end
#import "SQLManager.h"
#import <sqlite3.h>

@implementation SQLManager

static SQLManager *_instance;
+ (instancetype)shareManage
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[SQLManager alloc] init];
    });
    return _instance;
}

sqlite3 *_db;
+ (void)initialize
{
    // 1.拼接資料庫地址
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
    NSString *sqlFile = [path stringByAppendingPathComponent:@"student.sqlite"];
    // 2.開啟資料庫
    int result = sqlite3_open(sqlFile.UTF8String, &_db);// [self db]
    // 3.判斷是否開啟成功
    if (result == SQLITE_OK) {
        NSLog(@"開啟成功");
        // 3.1建立表
        NSString *sql = @"CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT , name TEXT, age INTEGER, score REAL);";
        result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, NULL);
    }
}

- (BOOL)insertStudent:(HMStudent *)student
{
    NSString *sql = [NSString stringWithFormat: @"INSERT INTO t_student(age, score, name) VALUES (%d, %f, '%@');", student.age, student.score, student.name];
    int result =  sqlite3_exec(_db, sql.UTF8String, NULL, NULL, NULL);
    if (result == SQLITE_OK) {
        return YES;
    }
    return NO;
}
- (BOOL)deleteWithStudent:(HMStudent *)student
{
    NSString *sql = [NSString stringWithFormat:@"DELETE FROM t_student WHERE id = %d;",  student.ID];
    int result =  sqlite3_exec(_db, sql.UTF8String, NULL, NULL, NULL);
    if (result == SQLITE_OK) {
        return YES;
    }
    return NO;
}
- (BOOL)updateWithStudent:(HMStudent *)student
{
    
    NSString *sql = [NSString stringWithFormat:@"UPDATE t_student SET name = '%@';", student.name];
    int result =  sqlite3_exec(_db, sql.UTF8String, NULL, NULL, NULL);
    if (result == SQLITE_OK) {
        return YES;
    }
    return NO;
}
- (NSArray *)query
{
    NSString *sql = @"SELECT * FROM t_student;";
    sqlite3_stmt *stemt = NULL;
    sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stemt, NULL);
    // 2.判斷有沒有查詢結果
    NSMutableArray *arrM = [NSMutableArray array];
    while (sqlite3_step(stemt) == SQLITE_ROW) {
        // 3.取出查詢到得結果
        const unsigned char *name = sqlite3_column_text(stemt, 1);
        int age = sqlite3_column_int(stemt, 2);
        double score = sqlite3_column_double(stemt, 3);
        HMStudent *stu = [[HMStudent alloc] init];
        stu.name = [NSString stringWithUTF8String:name];
        stu.age = age;
        stu.score = score;
        [arrM addObject:stu];
    }
    return arrM;
}
@end

FMDB的基本使用:

以OC的方式封裝了SQLite的C語言API.

優點是:

使用起來更加面向物件,省去麻煩冗餘的C語言的程式碼,對比蘋果的Core Data 框架,更加的輕量級和靈活,提供了多執行緒安全的資料庫操作方法,有效防止資料混亂。

FMDB有三個三個主要的類:

FMDataBase :一個FMDataBase 物件就代表一個單獨的DataBase資料庫,用來執行SQL語句。

FMResultSet: 使用FMResultSet執行查詢後的結果集。

FMDataBaseQueue: 用於多執行緒執行多個查詢或者更新,他是執行緒安全的。

@property (nonatomic, strong)  FMDatabase *db;
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    /*
     1.建立資料庫
     2.建立表
     3.操作
     */
    // 1.拼接資料庫地址
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
    NSString *sqlFile = [path stringByAppendingPathComponent:@"student.sqlite"];
    //該方法接收一個路徑, 它會根據地址建立一個數據庫, 如果不存在就會自動建立
    FMDatabase *db = [FMDatabase databaseWithPath:sqlFile];
    self.db = db;
    if([db open]){
        // 2.建立表
        NSString *sql = @"CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT , name TEXT, age INTEGER, score REAL);";

        // 注意: 在FMDB中除了查詢以外的操作都稱之為更新
        if([db executeUpdate:sql]){
            NSLog(@"建立表成功");
        }
    }
}
- (IBAction)insertClick:(id)sender
{  NSString *sql = @"INSERT INTO t_student(age, score, name) VALUES ('28', 100, 'jonathan');";
    if([self.db executeUpdate:sql])
    {
        NSLog(@"插入成功");
    }
}
- (IBAction)selectClick:(id)sender
{
    NSString *sql = @"SELECT * FROM t_student;";
    // 查詢資料, 會將所有查詢到得資料, 放到results中
    FMResultSet *results = [self.db executeQuery:sql];
    // 從results中獲取資料
    while ([results next]) {
        NSString *name = [results stringForColumn:@"name"];
        int age = [results intForColumn:@"age"];
        double score = [results doubleForColumn:@"score"];
        NSLog(@"%@ %d %f", name, age, score);
    }
}

FMDataBaseQueue:

@property (nonatomic, strong) FMDatabaseQueue *dbQueue;
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 1.拼接資料庫地址
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
    NSString *sqlFile = [path stringByAppendingPathComponent:@"student.sqlite"];
    // 建立一個數據庫佇列
    FMDatabaseQueue *dbQueue = [FMDatabaseQueue databaseQueueWithPath:sqlFile];
    self.dbQueue = dbQueue;
    //只要呼叫dbQueue的inDatabase方法, 系統就會傳遞一個已經開啟並且執行緒安全的資料庫給我們
    [dbQueue inDatabase:^(FMDatabase *db) {
        // 2.建立表
        NSString *sql = @"CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT , name TEXT, age INTEGER, score REAL);";
        
        // 注意: 在FMDB中除了查詢以外的操作都稱之為更新
        if([db executeUpdate:sql]){
            NSLog(@"建立表成功");
        }
    }];
}
- (IBAction)insertClick:(id)sender
{
    [self.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *sql = @"INSERT INTO t_student(age, score, name) VALUES ('28', 100, 'jonathan');";
        if([db executeUpdate:sql])
        {
            NSLog(@"插入成功");
        }
    }];
}
- (IBAction)selectClick:(id)sender
{
    [self.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *sql = @"SELECT * FROM t_student;";
        // 查詢資料, 會將所有查詢到得資料, 放到results中
        FMResultSet *results = [db executeQuery:sql];
        // 從results中獲取資料
        while ([results next]) {
            NSString *name = [results stringForColumn:@"name"];
            int age = [results intForColumn:@"age"];
            double score = [results doubleForColumn:@"score"];
            NSLog(@"%@ %d %f", name, age, score);
        }
    }];
}
@end

事務:可以將多條SQL資料繫結在一起,要麼一起執行成功要麼一起執行失敗。
CoreData:

Core Data框架提供了物件-關係對映(ORM)的功能,即能夠將OC物件轉化成資料,儲存在SQLite3資料庫檔案中,也能夠將儲存在資料庫中的資料還原成OC物件。在此資料操作期間,不需要編寫任何SQL語句。