[PR]
2025年06月18日
×
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
データベースのオープンと検索の基本
2008年11月22日
データベースのファイルが作れたら、まずそれを開きます。以下の例はKigenのソースを元に解りやすいように一部を書き換えたものです。何をやっているかというと、カテゴリリスト画面のUITableViewに表示するデータを格納する配列の作成です。ファイルを開いて、検索して、次の検索ができるように解放するところまでで、ファイルを閉じるところは含まれていません。ファイルはプログラム自体が終了するところで閉じるからです。
// lv1はカテゴリ1行ごとのデータ(下に出てくるNamaeというデータクラス)を格納する配列で、
// 本当はクラス内で宣言しています
NSMutableArray *lv1;
// databeseはデータベースのポインタで、これも本当はクラス内で宣言してます
// mydatabase.sqlに自分で用意するデータベースファイル名を入れてください
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
// lv1はカテゴリ1行ごとのデータ(下に出てくるNamaeというデータクラス)を格納する配列で、
// 本当はクラス内で宣言しています
NSMutableArray *lv1;
// databeseはデータベースのポインタで、これも本当はクラス内で宣言してます
sqlite3 *database;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"mydatabase.sql"];// mydatabase.sqlに自分で用意するデータベースファイル名を入れてください
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
// データベースファイルを開くことに成功しましたので、SQL文を構築します
// namaeというテーブルのデータでlevel2という列が-1になっている行から
// namaeというテーブルのデータでlevel2という列が-1になっている行から
// pkという列にあるデータ(重複不可能な行番号)を取り出します
const char *sql = "SELECT pk FROM namae WHERE level2=-1";
// SQL文を保持するポインタを作ります
sqlite3_stmt *statement;
sqlite3_stmt *statement;
// SQLで検索を実行する前準備です
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
// 準備が完了したので、実際に検索を実行します
// 検索条件に合う行があればSQLITE_ROWが返りループし続けます
// 検索条件に合う行があればSQLITE_ROWが返りループし続けます
while (sqlite3_step(statement) == SQLITE_ROW) {
// pkの値を変数primaryKeyに代入します。
int primaryKey = sqlite3_column_int(statement, 0);
// Namaeというデータクラスを記憶する場所を確保します
// Namaeというデータクラスを記憶する場所を確保します
// 確保されたデータクラスは自身で行に含まれている残りのデータを取り出します
Namae *namae = [[Namae alloc] initWithPrimaryKey:primaryKey
Namae *namae = [[Namae alloc] initWithPrimaryKey:primaryKey
database:database];
// 作成したNamaeクラスを配列に加えます
[lv1 addObject:namae];
[lv1 addObject:namae];
// namaeという名前のポインタはもう不要なので解放します
[namae release];
[namae release];
} // ここまでが検索で該当する行がある限り繰り返されます
}
// 検索が終わったのでSQL文を解放します
sqlite3_finalize(statement);
} else {
sqlite3_finalize(statement);
// 何かエラーが起きた場合にはデータベースを閉じます
sqlite3_close(database);
sqlite3_close(database);
NSAssert1(0, @"データベースのオープンに失敗 '%s'.", sqlite3_errmsg(database));
}
PR
Kigen Lite ver 1.5.0
2008年11月21日
タイトルでお解りでしょう。こちらもアップロードしました。アイテム数の制約からList Viewでの高速スクロールは全く意味を持ちませんが、メール送信機能などバージョンアップ内容はKigenと同じです。Kigenの弟分みたいなものですから、当然といえば当然ですね。
Kigen ver 1.5.0
2008年11月21日
本日、Kigenのver1.5.0をアップロードしました。内容に問題なしなら近日中にApp Storeにも出るでしょう。以前に少し書いたように、内部にある期限データをメール添付で出力する機能を追加しています。出力はカンマ区切りのcsvにする予定でしたが、米国の年月日表示はカンマが使用されるため、タブ区切りにしました。正確にはtsvということになるのですが、拡張子tsvというのはあまりメジャーではないのか某表計算ソフトから無視されました。そこで、MIMEはtext/tsvとしたのですが、拡張子はcsvにしています。
iPhone/iPod touchで添付ファイル付きメールを送るAPIは標準で用意されていないようなので、BSDソケットを使っての出力を自前で実装しています。標準的な25番ポートを用いたSMTPサーバーでのメール送信なら問題なく行えるはずで、失敗したらエラーが表示されます。うまくいった場合もメール送信完了のメッセージが出ます。
その他の特徴としては、List Viewのスクロール速度がかなり高速になっています。そして、アイテム画面でその下に登録されてる個数の合計を表示するようにしました。このことは内部のヘルプに書き忘れましたが…
このようにいろいろてんこ盛りなバージョンアップになるため、一気に1.5.0までバージョン番号が跳ね上がった次第です。
iPhone/iPod touchで添付ファイル付きメールを送るAPIは標準で用意されていないようなので、BSDソケットを使っての出力を自前で実装しています。標準的な25番ポートを用いたSMTPサーバーでのメール送信なら問題なく行えるはずで、失敗したらエラーが表示されます。うまくいった場合もメール送信完了のメッセージが出ます。
その他の特徴としては、List Viewのスクロール速度がかなり高速になっています。そして、アイテム画面でその下に登録されてる個数の合計を表示するようにしました。このことは内部のヘルプに書き忘れましたが…
このようにいろいろてんこ盛りなバージョンアップになるため、一気に1.5.0までバージョン番号が跳ね上がった次第です。
データベースのオープンと検索の概要
2008年11月20日
SQLite3データベースの開き方はアップル提供のサンプルSQLite Book Listが参考になります。AppDelegate.mにあるcreateEditableCopyOfDatabaseIfNeededをそのまま再利用することができます。ファイル名を自分がリソースとして加えたデータベースファイルのものに変更するだけです。これでまずデータベースファイルが必ず存在する状態(発見できなければ作成する)にします。
実際にデータベースを開いてSQL文でデータを取り出す操作はinitializeDatabaseが参考になります。基本的な流れとしては、まずファイルをsqlite3_open関数で開いたあと、SQL文を作り、それを準備用の関数に渡します。文の中に変数(”?”と表記した部分)がある場合は、そこの部分に実際の変数を埋め込みます。そして実際に検索を行います。データベースに検索条件に一致するデータがあった場合には、それを順番に読み出します。データの読み出しが終わったらSQL文をデータベースから解放します。
データベースファイルを最終的に閉じる方法はapplicationWillTerminateにあるように、sqlite3_close関数を実行します。ファイルを開いたときと閉じたとき、問題が無い場合はSQLITE_OKが返ってきます。
2008.12.8追記
SQLite Book ListのcreateEditableCopyOfDatabaseIfNeededでやっているのは、ユーザーのデータ用フォルダにファイルが無い場合にリソースのSQLファイルをコピーする動作でした。初回の起動時に行われるだけで、既にユーザーデータがある場合はコピーをしません。そのため、バージョンアップを行ってもユーザーが作ったデータにサンプルデータを上書きしてしまう、というような事態は避けられるようになっています。
実際にデータベースを開いてSQL文でデータを取り出す操作はinitializeDatabaseが参考になります。基本的な流れとしては、まずファイルをsqlite3_open関数で開いたあと、SQL文を作り、それを準備用の関数に渡します。文の中に変数(”?”と表記した部分)がある場合は、そこの部分に実際の変数を埋め込みます。そして実際に検索を行います。データベースに検索条件に一致するデータがあった場合には、それを順番に読み出します。データの読み出しが終わったらSQL文をデータベースから解放します。
データベースファイルを最終的に閉じる方法はapplicationWillTerminateにあるように、sqlite3_close関数を実行します。ファイルを開いたときと閉じたとき、問題が無い場合はSQLITE_OKが返ってきます。
2008.12.8追記
SQLite Book ListのcreateEditableCopyOfDatabaseIfNeededでやっているのは、ユーザーのデータ用フォルダにファイルが無い場合にリソースのSQLファイルをコピーする動作でした。初回の起動時に行われるだけで、既にユーザーデータがある場合はコピーをしません。そのため、バージョンアップを行ってもユーザーが作ったデータにサンプルデータを上書きしてしまう、というような事態は避けられるようになっています。
セルの各行に表示するテキスト
2008年11月19日
テーブルの各行に表示するテキストはcellForRowAtIndexPathで知らせます。この命令の引数はindexPathだけですが、indexPathにはindexPath.sectionにセクションのインデックス、indexPath.rowに行のインデックスがそれぞれ格納されています。ですから、indexPath.sectionとindexPath.rowで場合分けをしてあげれば良いのです。デフォルトの設定だと、各セルには1行のテキストが入ります。テキストの中身はcell.textに入れて、最後にcell全体を返します。例は長くなるので途中を省略します。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
switch (indexPath.section) {
case 0: // 一番上のセクションです
switch (indexPath.row) {
case 0: // テーブルの1行目です
cell.text = @"一番上のセクションで一番上の表示項目です";
break;
break;
case 1: // テーブルの2行目です
cell.text = @"一番上のセクションで2番目の表示項目です";
break;
break;
} // 一番上のセクション終了
case 1: // 2番目のセクションです
(途中略)
} // 4番目のセクション終了
}
return cell;
}
セクションごとの行数
2008年11月19日
セクションごとの行数はnumberOfRowsInSectionで知らせます。どのセクションも同じ行数なら、単純に一行で済みます。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 3; // 各セクションとも3行からなります。
}
ただ、実際にそんなことは滅多にないでしょう。その場合はsectionの値で分岐させて値を返します。この場合も前回と同様セクションが4つある場合です。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
ただ、実際にそんなことは滅多にないでしょう。その場合はsectionの値で分岐させて値を返します。この場合も前回と同様セクションが4つある場合です。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
switch (section) {
case 0:
return 2; // 一番上のセクション項目は2行です。
break;
case 1:
return 3; // 上から2番目のセクション項目は3行です。
break;
case 2:
return 1; // 上から3番目のセクション項目は1行です。
break;
case 3:
return 3; // 上から4番目のセクション項目は3行です。
break;
}
}
セクションに表示する文字列
2008年11月19日
セクションに表示する文字列はtitleForHeaderInSectionで知らせます。この命令が呼ばれるときに、sectionにはセクションのインデックスが入っています。一番上のインデックスは0になります。以前の例のようにセクションが4つある場合には、下のような感じにすると良いのではないでしょうか。
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *str;
switch (section) {
switch (section) {
case 0:
str = @"一番上に表示されるセクションタイトル";
break;
break;
case 1:
str = @"上から2個目のセクションタイトル";
break;
break;
case 2:
str = @"上から3個目のセクションタイトル";
break;
break;
case 3:
str = @"上から4個目のセクションタイトル";
break;
}
return str;
return str;
}
テーブルのセクション数
2008年11月19日
テーブルのセクション数はnumberOfSectionsInTableViewで返す値で知らせます。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
プログラマ側でいじるのは返す値だけです。上の例では、このテーブルには4つのセクションがありますよと知らせているわけです。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 4;
}
プログラマ側でいじるのは返す値だけです。上の例では、このテーブルには4つのセクションがありますよと知らせているわけです。