ラベル FMDB の投稿を表示しています。 すべての投稿を表示
ラベル FMDB の投稿を表示しています。 すべての投稿を表示

2014年9月8日月曜日

Swift環境からObjective-Cで書かれたCocoaPodsを使用する
(SwiftでFMDBを使う)

Swift環境からObjective-Cで書かれたCocoaPodsのライブラリを利用してみる
ライブラリはFMDBを取り込んで、FMDBを使用した簡単なプログラムをSwiftを用いて作成します。
空プロジェクトの作成
まずは、空プロジェクトを作成し、そしてすぐに閉じます。CocoaPodsでFMDBを取り込むプロジェクトファイルだけを前もって作成します。

※因みに本サンプルはXcode6 beta7にて作成しています。
podfileの作成とインストール
podfileの内容を以下のようにして作成したプロジェクトの直下に作成し、
pod 'FMDB'
以下コマンドを実行してインストールを実行。
% pod install
インストールしたら、作成されたワークスペース(プロジェクトファイルじゃないです)を開きます。 下記の様にコマンドラインから起動しても良いですし、FinderからXXX.xcworkspaceファイルをダブルクリックしても良いです。
% open XXX.xcworkspace
ワークスペースを起動したら、以下のようにPodsプロジェクトが作成されていることを確認して下さい。
※CocoaPodsは既にインストール済みを前提とします。CocoaPodsのインストールやそのた詳細についてはCOCOAPODSを参照。

Bridging Headerの作成
Objective-CをSwift環境から利用するにはBridging Headerを作成する必要があります。
このヘッダにSwift環境で使用するObjective-Cのヘッダファイルを記載することになります。
Bridging-Headerは、
1)仮のObjective-Cのファイルを作成することで自動作成、自動設定する方法(自動生成)
2)個別に作成し設定する方法(独自作成)
があります。
Bridging Headerを自動生成する場合は
Objective-Cのファイルを生成します。 Xcode menu > File > New > File... を選択しファイル新規作成します。 以下の様に、Objective-C Fileを選択しNextを押下。
ファイル名は後ほど削除するので、何でも良いです。
すると、以下の様に bridging header を作成するか問われるのでYESを押下します。
以下の様に bridging headerが作成されます。
以上で完了です。後はObjective-Cのヘッダファイルをbridging headerに記載するのみです。 FMDBの場合は以下のようにすれば良いでしょう。
//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "FMDatabase.h"
作成したdummy.mファイルは削除して構いません。
Bridging Headerを独自に作成、設定する場合は
まずはbridging headerを作成します。 ファイル名はなんでも良いですが、bridging headerと分かるようにしましょう。
作成したら、BuildSettingを変更します。作成したプロジェクトを選択して、Swift Conpiler - Code Generation の設定項目値の Objective-C Bridging Header の項目を編集します。
ヘッダの内容は自動設定の時と同じです。自前でヘッダを作成すると#ifdef/#endifが自動挿入されますが、特に削除して問題無いでしょう。(自動作成時に存在しなかったので)
//  Bridging-Header.h
//  UsingCocoaPodsInSwift
//
#import "FMDatabase.h"

以上で、準備は終わりです。
SwiftからFMDBのメソッド呼び出し
あとはSwiftでコードが書いていくだけです。FMDBが提供しているメソッドの呼び出しはSwift言語仕様に変換されて呼び出すことになりますが、Xcodeのコード補完の機能があるので、メソッド名さえ分かっていればそれほど難しくはないでしょう。
抜粋しますが、FMDBに関するメソッドとして、初期化と更新クエリの呼び出し箇所を載せておきます。詳細は後述するGitHub上のソースコードを参照下さい。

FMDBインスタンス生成
        m_db = FMDatabase(path:m_dbPath)
トランザクションを開始してインサート処理
    func createNewContent(content:String) -> Bool {
        if m_db.goodConnection() {
            m_db.beginTransaction()
            m_db.executeUpdate("insert into memo (contents, 'update') values (?, ?)", withArgumentsInArray: [content, NSDate()])
            m_db.commit()
            //OK
            return true
        }
        //FAIL, database not opend.
        return false;
    }
詳細はGitHubに上げていますので、適宜参照して下さい。
https://github.com/takuran/UsingCocoaPodsInSwift
以下画面サンプル。

2012年5月8日火曜日

iOSでSQLiteを使う2(FMDBのFMDatabaseQueueクラスを使ってみる)

FMDBにはスレッド間でSQLiteを使用する際のヘルパークラスが提供されています。そのクラスがFMDatabaseQueueです。 このクラスの用途と、どういったシーンで使用していけば良いのかを考えつつ、サンプルプログラムで試してみます。

因みに、FMDBの基本的な使い方に関しては前回書いた iOSでSQLiteを使う(FMDB) を参照。
マルチスレッドにおけるFMDatabaseインスタンスに関して

FMDBのREADMEにはFMDatabaseのインスタンスをシングルトンにして、マルチスレッド上から唯一のFMDatabaseインスタンスを 取得して使用することは推奨されておらず、一見うまく動いているようでも、いつかはクラッシュしたり、例外が 発生することになるだろうと書かれています。つまり、FMDatabaseクラスはスレッドセーフでは無いということになります。

ちなみに、シングルトンじゃなくてスレッド毎にFMDatabaseインスタンスを生成すれば良いんじゃ?という考え方もあるかもしれない。 確かにそのアプローチもアリかと思いますが、FMDBのトランザクションが読み取り・書き込み排他ロックをデフォルトに してるっぽいので、複数インスタンスを生成してもDBへのアクセスは結局排他されるのであまり意味が無く、余計にメモリ を使用してしまうだけだと思う。(beginTransactionでトランザクションを開始した場合)
beginDeferredTransactionにてトランザクションを開始した場合は、読み取りは共有ロックになるので、 更新中の読み取りが可能になりますが、そもそもSQLiteへのアクセスは自身のアプリケーションからのみであるため そうそう更新中の読み取りを実施したいという要望は無いはず。それより、SQLiteへのアクセスは直列化してアクセスさせ、 処理をシンプルにする方がメリットが大きいのではないでしょうか。

試しにマルチスレッド上で同一のFMDatabaseインスタンスを使ってみる

以下のコードは、DBを検索してその結果を文字列としてUITextView上に表示するという処理を 10個の新たなスレッド上で実行させています。 ちなみに、以下コードのDB検索部分を載せていませんが 前回の記事 と同じものです。

    //結果取得して画面描画を別スレッドで行う
    //スレッドはNSOperationQueueにて生成
    NSOperationQueue *opeQueue = [[NSOperationQueue alloc]init];

    //同期用のオブジェクト(インデックス表示用)
    NSObject *sync = [[NSObject alloc]init];
    static int index = 0;

    //スレッド実行部分のブロック処理
    void (^exec)(void)  = ^(void) {
        int _index = 0;
        @synchronized(sync) {
            index++;
            NSLog(@"新しいスレッド。index : %d.", index);
            _index = index;
        }
        
        //DB検索を実行
        NSArray *result = [db executeQuery2];
        //検索結果の文字列を作成
        NSString *text = [NSString stringWithFormat:@"%d\n", _index];
        for (ExampleRecord *record in result) {
            text = [text stringByAppendingString:
                    [NSString stringWithFormat:@"%d, %@, %@, %f\n",
                        record.columnOfInteger,
                        record.columnOfDate,
                        record.columnOfText,
                        record.columnOfReal]];
        }
        
        //特に意味ないけど、2秒スリープ
        [NSThread sleepForTimeInterval: 2.0];
        //画面に表示。メインスレッド以外からの更新なのでperformSelectorOnMainThreadを使用。
        if ([result count] != 0) {
            [self performSelectorOnMainThread:@selector(updateTextView:) withObject:text waitUntilDone:NO];
        }

    };//end of the block.
    
    //新しいスレッドを生成し実行
    [opeQueue addOperationWithBlock: exec];
    [opeQueue addOperationWithBlock: exec];
    [opeQueue addOperationWithBlock: exec];
    [opeQueue addOperationWithBlock: exec];
    [opeQueue addOperationWithBlock: exec];
    [opeQueue addOperationWithBlock: exec];
    [opeQueue addOperationWithBlock: exec];
    [opeQueue addOperationWithBlock: exec];
    [opeQueue addOperationWithBlock: exec];
    [opeQueue addOperationWithBlock: exec];

上記処理を何度も実行すると以下のような例外が発生します。以下は例外時のイメージですが、 結構何度も実行させてやっと発生した感じです。通常は例外が発生しないでFMDBの各メソッドがエラー返却される パターンが殆どでした。まあ、エラーになることはこれで分かりました。


・デバッガ

・標準出力結果(backtrace)
FMDatabaseQueueを使用して実行してみる

マルチスレッド上からFMDatabaseQueueを使用してDB検索を実行してみます。

    //結果取得して画面描画を別スレッドで行う
    //スレッドはNSOperationQueueにて生成
    NSOperationQueue *opeQueue = [[NSOperationQueue alloc]init];

    //同期用のオブジェクト(インデックス表示用)
    NSObject *sync = [[NSObject alloc]init];
    static int index = 0;

    //スレッドで実行するブロック定義
    void (^exec)(void) = ^(void) {
        int _index=0;
        //各スレッドで表示するインデックスインクリメント
        //ここはログ表示用なのであまり気にしないで下さい。
        @synchronized(sync) {
            index++;
            _index = index;
        }
        NSLog(@"新しいスレッド。index : %d.", _index);        
        
        //SQLを実行。以下のメソッド内でFMDatabaseQueueクラスを使用しています。
        [db executeQuery3:self index:_index];
    };
    
    //スレッド実行
    [opeQueue addOperationWithBlock:exec];
    [opeQueue addOperationWithBlock:exec];
    [opeQueue addOperationWithBlock:exec];
    [opeQueue addOperationWithBlock:exec];
    [opeQueue addOperationWithBlock:exec];
    [opeQueue addOperationWithBlock:exec];
    [opeQueue addOperationWithBlock:exec];
    [opeQueue addOperationWithBlock:exec];
    [opeQueue addOperationWithBlock:exec];
    [opeQueue addOperationWithBlock:exec];

実際にDBアクセス部分(上記のexecuteQuery3というメソッド)のコードは以下になります。 以下コードで、_dbQueueという変数がFMDatabaseQueueのインスタンスです。インスタンス生成部分のコードは 別途示します。

- (void)executeQuery3:(id)delegate index:(int)index {
    if (_dbQueue) {

        //トランザクション無しのパターン
        //_dbQueue変数はFMDatabaseQueueのインスタンスです。生成は別途実施済みです。
        [_dbQueue inDatabase:^(FMDatabase *db) {
            FMResultSet* rs = [db executeQuery:@"select * from example where t like ?", @"%2."];
            
            if ([db hadError]) {
                NSLog(@"Err %d: %@",
                      [db lastErrorCode], [db lastErrorMessage]);
            }

            NSString *text = [NSString stringWithFormat:@"%d\n", index];
            while ([rs next]) {
                text = [text stringByAppendingString:
                        [NSString stringWithFormat:@"%d, %@, %@, %f\n",
                                [rs intForColumnIndex:0],
                                [rs dateForColumnIndex:1],
                                [rs stringForColumnIndex:2],
                                [rs doubleForColumnIndex:3]]];
            }

            //sleep
            [NSThread sleepForTimeInterval:2.0];
            
            //update UI.
            [delegate performSelectorOnMainThread:@selector(updateTextView:) withObject:text waitUntilDone:NO];
        }];
        
        NSLog(@"call finished. index : %d.", index);
    }
}

このコードで何度か実行しましたが、例外やFMDBのクラスからエラーが発生することはありませんでした。 実行すると処理内で2秒スリープしているため2秒間隔で画面が更新されていきます(以下、赤字部分)。

・標準出力の結果
2012-05-08 06:52:00.515 FMDBTest[15557:f803] ViewDidLoad was finished.
2012-05-08 06:52:00.569 FMDBTest[15557:12e03] 新しいスレッド。index : 1.
2012-05-08 06:52:00.570 FMDBTest[15557:12f03] 新しいスレッド。index : 2.
2012-05-08 06:52:00.570 FMDBTest[15557:11103] 新しいスレッド。index : 3.
2012-05-08 06:52:00.571 FMDBTest[15557:13003] 新しいスレッド。index : 4.
2012-05-08 06:52:00.576 FMDBTest[15557:13203] 新しいスレッド。index : 5.
2012-05-08 06:52:00.580 FMDBTest[15557:13c03] 新しいスレッド。index : 6.
2012-05-08 06:52:00.588 FMDBTest[15557:14403] 新しいスレッド。index : 8.
2012-05-08 06:52:00.596 FMDBTest[15557:1450b] 新しいスレッド。index : 10.
2012-05-08 06:52:00.587 FMDBTest[15557:14203] 新しいスレッド。index : 7.
2012-05-08 06:52:00.595 FMDBTest[15557:14303] 新しいスレッド。index : 9.
2012-05-08 06:52:02.585 FMDBTest[15557:12e03] call finished. index : 1.
2012-05-08 06:52:04.588 FMDBTest[15557:12f03] call finished. index : 2.
2012-05-08 06:52:06.590 FMDBTest[15557:11103] call finished. index : 3.
2012-05-08 06:52:08.591 FMDBTest[15557:13003] call finished. index : 4.
2012-05-08 06:52:10.593 FMDBTest[15557:13203] call finished. index : 5.
2012-05-08 06:52:12.594 FMDBTest[15557:13c03] call finished. index : 6.
2012-05-08 06:52:14.596 FMDBTest[15557:14403] call finished. index : 8.
2012-05-08 06:52:16.598 FMDBTest[15557:1450b] call finished. index : 10.
2012-05-08 06:52:18.600 FMDBTest[15557:14203] call finished. index : 7.
2012-05-08 06:52:20.602 FMDBTest[15557:14303] call finished. index : 9.


・シュミレーター画面

上のコードをちょっと解説します。

・FMDatabaseQueueの生成部分
上記のサンプルコードには表れていませんが、FMDatabaseQueueのインスタンス生成はFMDatabaseの生成時と同じでDBパスを引数に指定します。
        _dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
・FMDatabaseQueueへの処理依頼
処理はブロックで記載する必要があります。ここでいきなりブロックとは?となるかもしれませんが、、 すみません、ここではブロックの解説はしません。 FMDatabaseQueueは主に以下の2つのメソッドを提供しています。
  • - (void)inDatabase:(void (^)(FMDatabase *db))block
  • - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;
トランザクションを必要としない場合(inDatabase)とする場合(inTransaction)です。 トランザクションを必要とする場合はロールバック有無を表す引数が存在します。 ちなみにこのrollbackの変数はC言語のポインタ型なので、値を設定する際は実体に設定する必要がありますので、 注意して下さい。

以下にFMDBのREADMEに載っているサンプルコードを示します。実体に値を設定するには *(アスタリスク)が必要です。#C/C++をご存知のかたは特に注意する必要もないですが
    [queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
                [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
                [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
                [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];

                if (whoopsSomethingWrongHappened) {
                    //
                    //↓ここです。実体に設定するには*が必要です。
                    *rollback = YES;
                      return;
                }
                // etc…
                [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]];
    }];
FMDatabaseQueueクラスの機能

FMDatabaseQueue.mのソースを見れば分かりますが、このクラスは次の機能を提供します。

・直列ディスパッチキューを生成
Grand Central Dispatch(GCD)の直列ディスパッチキューを生成します。この直列キューはタスクを同時には 1つしか実行しないので、キューに登録した処理は必ず同期処理されます。つまり、FMDatabaseQueueは全てのSQLite へのアクセスを完全同期していることになります。

・同期型で実行
直列ディスパッチキューからの実行は同期型(dispatch_sync)で実行しているので、呼び出し元スレッドは ブロックされるので注意する必要があります。私はこの部分を勘違いしており、ブロックで指定したタスクは FMDBのQueue側で非同期に実行してくれるものだと勘違いしていました。つまり、重そうなSQLをFMDatabaseQueue にまかせれば後は勝手に非同期でやってくれる訳では無いということです。スレッドは自分で作成する必要があります。


2012年4月25日水曜日

iOSでSQLiteを使う(FMDB)

SQLiteのライブラリはC言語のライブラリとして提供されています。したがって、SQLiteの各関数の呼び出しや引数の指定方法、パラメータの型はC言語の文法・型に従う必要があります。

特にC言語への型変換に関して、DBの場合は数値(整数、浮動小数点数)、文字列、バイナリ、日付等様々な型があり、Cocoa TouchからC言語への型変換をSQL実行時と結果取得時に行う必要があるため、結構面倒だったりします。

そんなこともあってか、iOSのSQLiteラッパーライブラリとしてFMDBというライブラリがあり、このライブラリはこの面倒な型変換を全て受け持ってくれるため、この点だけでもFMDBを使う魅力は大いにあると思います。(注:後述しますが、FMDBは型変換だけでは無いです)
また、FMDBはARC有効/無効どちらにも対応しています。プリプロセッサでARCの有効状態を判定しているみたいですね。

素でいくかFMDBを利用するかの方針決定
 私は素でSQLiteを使う方法とFMDBを使う方法を試しましたが、やはりFMDBを使ったほうがコードが簡単になりますし、なによりもCocoa Touchのクラスを直接使うことが可能である点とマルチスレッド用のクラスやDBプーリングのクラスも提供されていることからFMDBを素直に使用したほうが結局のところ効率が良いと感じました。
ちなみにFMDBのライセンスはMITライセンスです。
以降はFMDBを使用することを前提としています。
FMDBのダウンロード
=========== 2014/3/31 追記 ===================
FMDBのインストールについて追記します。
以下、本記事に沿ってインストールしても問題はありませんが、CocoaPodsでインストールされることをオススメします。 OS Xはデフォルトでrubyがインストールされているので、以下コマンドですぐに利用できます。
・CocoaPodsのインストール
$ sudo gem install cocoapods
・FMDBのインストール
//FMDBをインストールしたいあなた自身が作成したプロジェクト配下へ移動します
$ cd [インストールしたいあなたのプロジェクト]
$ vi Podfile
Podfileを作成する。中身は以下。最新版をインストールするなら以下で良いでしょう。
pod 'FMDB'
・インストール実行
$ pod install
xcodeでワークスペースとして起動する。
$open YourApp.xcworkspace
するか Finderで拡張子がxcworkspaceとなっている方を起動すると 既にFMDBがインストールされた状態となっているはずです。後はヘッダファイルを引き込めばOKですね。 因みに、CocoaPodsでインストールした場合は本記事の「FMDBのダウンロード」と「SQLiteのライブラリ取り込みとFMDBラッパークラスファイルの取り込み」の章はスキップして下さい。
Reference : CocoaPods.
=============== 追記終わり ==================
 FMDBはライブラリと言いましたが、ソースコードとして提供されているので、ダウンロードして必要なファイルをプロジェクトに追加します。
 ソースはgithubで公開されています。https://github.com/ccgus/fmdbブラウザでここを開いて直接ZIPファイルをダウンロードしても良いですし、Xcodeをインストールしているのであれば恐らくgitコマンドがインストールされていると思われるので以下の様にコマンドでgithubのFMDBプロジェクトのcloneをローカルに作成しても良いと思います。

 gitを使う場合コマンドプロンプトを起動して、以下を実行します。実行カレントディレクトリ配下にfmdbのディレクトリが作成されます。
%git clone https://github.com/ccgus/fmdb.git
 Xcode用のプロジェクトファイルもあるのでそのままXcodeで起動してコンパイル確認してみても良いですね。またテストコード用のソースコード(fmdb.m)も付属されており、このソースはFMDBを利用するに大変参考になります。
SQLiteのライブラリ取り込みとFMDBラッパークラスファイルの取り込み

・SQLiteのライブラリを追加

・FMDBのソースファイルをプロジェクトに追加
FMDB関連ファイルを格納するGroupフォルダを作成して格納した方がスッキリする。
DBファイルの事前作成とDBファイルのプロジェクト取り込み
 SQLiteのDBファイルは事前に作成して良いし、アプリケーション上で作成しても良いです。今回は事前に作成しています。
コマンドプロンプトで以下を実行し、初期スキーマを生成しテーブルを一つ作成します。
MacBook-Pro:tmp naoyuki$ sqlite3 sample.db 
SQLite version 3.7.7 2011-06-25 16:35:41Enter ".help" for instructionsEnter SQL statements terminated with a ";"
sqlite>create table example (i integer, n numeric, t text, r real);
sqlite> .schema
CREATE TABLE example (i integer, n numeric, t text, r real);
sqlite> .quit
MacBook-Pro:tmp naoyuki$ 

スキーマはこんな感じです。
CREATE TABLE example (
        i integer,
        n numeric,
        t text,
        r real
);
 前述で作成したDBファイル(sample.db)をプロジェクトに取り込みます。プロジェクトへの取り込みはメニューの「File」-「Add Files to XXX」で表示されるダイアログから該当ファイルを選択してプロジェクトへ取り込みを行います。
初回起動時はDBファイルをコピーしてからDBオープン
 ここまで長くなりましたがやっとここからコードを書きます。
まず初回時はDBスキーマが格納されているDBファイルを/Documents/配下へコピーする必要があります。このへんの詳細は、 Resource Bundle/リソースバンドルiOS 標準ディレクトリパスの取得あたりを参照してもらえると理解出来るとおもいます。
 以下、サンプルコードです。Document配下にDBファイルが無い場合はアプリケーションディレクトリからDBファイルをコピーしてからDBをオープンしています。
//DBファイル名
static NSString* const DB_FILE = @"sample.db";

@implementation SqliteExecute {
    FMDatabase*     _db;
}

- (BOOL)openDatabase {
    //DBファイルへのパスを取得
    //パスは~/Documents/配下に格納される。
    NSString *dbPath = nil;
    NSArray *documentsPath = NSSearchPathForDirectoriesInDomains
                                (NSDocumentDirectory, NSUserDomainMask, YES);
    //取得データ数を確認
    if ([documentsPath count] >= 1) {
        //固定で0番目を取得でOK
        dbPath = [documentsPath objectAtIndex:0];
        //パスの最後にファイル名をアペンドし、DBファイルへのフルパスを生成。
        dbPath = [dbPath stringByAppendingPathComponent:DB_FILE];
        NSLog(@"db path : %@", dbPath);
    } else {
        //error
        NSLog(@"search Document path error. database file open error.");
        return false;
    }
    
    //DBファイルがDocument配下に存在するか判定
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager fileExistsAtPath:dbPath]) {
        //存在しない
        //デフォルトのDBファイルをコピー(初回のみ)
        //ファイルはアプリケーションディレクトリ配下に格納されている。
        NSBundle *bundle = [NSBundle mainBundle];
        NSString *orgPath = [bundle bundlePath];
        //初期ファイルのパス。(~/XXX.app/sample.db)
        orgPath = [orgPath stringByAppendingPathComponent:DB_FILE];
        
        //デフォルトのDBファイルをDocument配下へコピー
        if (![fileManager copyItemAtPath:orgPath toPath:dbPath error:nil]) {
            //error
            NSLog(@"db file copy error. : %@ to %@.", orgPath, dbPath);
            return false;
        }
    }
    
    //open database with FMDB.
    _db = [FMDatabase databaseWithPath:dbPath];
    return [_db open];
}
更新処理
FMDBのメソッドは大きく分けて更新系のメソッドと参照系のメソッドに分かれています。

  • 更新系:- (BOOL)executeUpdate:(NSString*)sql, ...;
  • 参照系:- (FMResultSet *)executeQuery:(NSString*)sql, ...;

  •  上記メソッドは代表的なもので、この他にも引数が異なる形でいくつか提供されています。
    以下、更新系のサンプルです。トランザクションを開始している点とステートメント再利用フラグを有効にしている点に注意して下さい。
    - (BOOL)executeUpdate1 {
        BOOL result = TRUE;
        //トランザクション開始(exclusive)
        [_db beginTransaction];
        
        //ステートメントの再利用フラグ
        //おそらくループ内で同一クエリの更新処理を行う場合バインドクエリの準備を何回
        //も実行してしまうのためこのフラグを設定する。
      //このフラグが設定されているとステートメントが再利用される。
        [_db setShouldCacheStatements:YES];
        
        
        //insertクエリ実行(プリミティブ型は使えない)
    //    [_db executeUpdate:@"insert into example values (?, ?, ?, ?)",
    //                                1, 2, @"test", 4.1];
    // executeUpdateWithFormatメソッドで可能。
    
        for (int i=1; i<=30; i++) {
            [_db executeUpdate:@"insert into example values (?, ?, ?, ?)",
                                [NSNumber numberWithInt:i],
                                [NSDate date],
                                [NSString stringWithFormat:@"string : %d.", i],
                                [NSNumber numberWithFloat:(float)i/3]];
            //check
            if ([_db hadError]) {
                result = FALSE;
                NSLog(@"Err %d: %@", [_db lastErrorCode], [_db lastErrorMessage]);
            }
        }
        
        //commit
        [_db commit];
        
        return result;
    }
    
    
    トランザクションは2種類のモードが提供されており、以下メソッドが提供されています。
    - (BOOL)beginTransaction;
    - (BOOL)beginDeferredTransaction;

    ・beginTransaction
    begin exclusive transaction を指定してトランザクションを開始。
    これは読み取り、書き込み共に排他されるロックモードです。
    通常1スレッドで処理を行うのであれば、このモードで問題ありません。

    ・beginDeferredTransaction
    begin deferred transaction を指定してトランザクションを開始。
    これは必要に応じて最初のアクセス時にロックが働き、読み取りの場合は共有ロックで書き込みの場合は予約ロックがかけられるようです。因みにSQLiteのデフォルトロックモードは「Deferred」です。
    参照処理
    前述で登録したレコードを参照してみます。

    ・結果の取得をインデックスで行う場合。
    - (NSArray*)executeQuery1 {
        //結果格納用配列
        NSMutableArray *result = [[NSMutableArray alloc] init];
        
        //クエリの実行と結果(ResultSet)の取得
        FMResultSet *rs = [_db executeQuery:@"select * from example"];
        
        //結果の取得(indexで取得)
        while ([rs next]) {
            //結果格納用のオブジェクト
            ExampleRecord *record = [[ExampleRecord alloc]init];
            record.columnOfInteger = [rs intForColumnIndex:0];
            record.columnOfDate = [rs dateForColumnIndex:1];
            record.columnOfText = [rs stringForColumnIndex:2];
            record.columnOfReal = [rs doubleForColumnIndex:3];
            
            //配列に格納
            [result addObject:record];
        }
        //close ResultSet.
        [rs close];
        
        return result;
    }
    
    

    ・結果の取得をカラム名で行う場合
    - (NSArray*)executeQuery2 {
        //結果格納用配列
        NSMutableArray *result = [[NSMutableArray alloc]init];
        
        
        //クエリ実行
        FMResultSet *rs = [_db executeQuery:@"select * from example where i = ?", 
                           [NSNumber numberWithInt:2]];
    //    FMResultSet *rs = [_db executeQuery:@"select * from example where t like ?", @"%2."];
    
        if ([_db hadError]) {
            NSLog(@"Err %d: %@", [_db lastErrorCode], [_db lastErrorMessage]);
        }
        
        //結果の取得(カラム名指定)
        while ([rs next]) {
            //結果格納用オブジェクト
            ExampleRecord *record = [[ExampleRecord alloc]init];
            record.columnOfInteger = [rs intForColumn:@"i"];
            record.columnOfDate = [rs dateForColumn:@"n"];
            record.columnOfText = [rs stringForColumn:@"t"];
            record.columnOfReal = [rs doubleForColumn:@"r"];
            
            [result addObject:record];
        }
        //close ResultSet.
        [rs close];
        
        return result;
    }
    
     因みに、上のコードで登場しているExampleRecordというクラスは自前のクラスでレコードデータを格納する クラスです。レコードクラスは無理に作成する必要はありませんが、参考までに載せておきます。

    ・ExampleRecord.h
    @interface ExampleRecord : NSObject {
        int         i_;
        NSDate      *d_;
        NSString    *t_;
        double      r_;
    }
    
    // I'm using ARC.
    @property (nonatomic) int columnOfInteger;
    @property (nonatomic) NSDate* columnOfDate; 
    @property (nonatomic, copy) NSString* columnOfText;
    @property (nonatomic) double columnOfReal;
    
    @end
    

    ・ExampleRecord.m
    #import "ExampleRecord.h"
    
    @implementation ExampleRecord
    @synthesize columnOfInteger = i_;
    @synthesize columnOfDate = d_;
    @synthesize columnOfText = t_;
    @synthesize columnOfReal = r_;
    
    @end
    
    DBの解放
    - (void)closeDatabase {
        if (_db) {
            [_db close];
        }
    }
    

    ちょっと長くなってしまったので、続きは次回ということで。スレッド絡みは気になる所なので、そのあたりができたらと考えています。
    マルチスレッド上での使用を考慮したFMDatabaseQueueについては iOSでSQLiteを使う2(FMDBのFMDatabaseQueueクラスを使ってみる)の方に書いています。

    =========== 2014/4/22 追記 ===================
    FMDBを利用したサンプルプロジェクトをGitHubに上げていますので、参考にしてください。別件で作成したプロジェクトなので、若干本記事と異なる部分がありますが、初期化、参照、更新、バックグラウンド更新の処理があります。簡単なTableViewにAlertViewから入力した文字を表示するだけのアプリです。
    GitHub : https://github.com/takuran/FMDBExample
    $ git clone https://github.com/takuran/FMDBExample.git
    $ cd FMDBExample
    $ git submodule update --init --recursive
    して下さい。
    スクリーンショット:
    削除はスワイプでね。
    =============== 追記終わり ==================