スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

iPhoneでC++11を使う

なかなか面白いネタを提供出来ずにすみません。
最近は FrontierMail を libEtPan を使わずに書き直していまして、そちらに注力しています。
libEtPanは優れたライブラリですが、その分利用しないコードも多いので、これを簡略化して低レイヤ処理部分を含めて全て自分の方でメンテ出来るライブラリにしようとしております。

そこで、C++ベースで作成をしているのですが、
C++の03以前のものはご存じの通り、マルチスレッド処理を言語仕様としてサポートしておりません。
それをC++11からはサポートしています。

そんなC++11は boost ライブラリの機能からもいくつか採用しており、
特によく利用する機会の多い shared_ptr も標準でサポートしています。

要はこれをiPhoneでも利用したかったのですが、そのために boost をわざわざ組み込んで配布するのも少々手間だと考えていたところ、よく見たらiOS SDK 5.0 あたりからC++11がサポートされているのでこちらを利用することにしました。

利用手順としては単純で、以下のように
「C++ Language Dialect」と「C++ Standard Library」をC++11のものに変更するだけです。

iPhone C++11

但し、1点注意として、
C++11はDeployment Targetが 5.0 以上の場合でないと利用出来ません。
つまり、iOS 4.x以下ではC++11は利用できないのです。

ただ、秋にiOS 6も出るという状況を垣間見ると
iOS 4.xの寿命もそろそろというところなので、
積極的にiOS 5以上をターゲットにしていっても良いとは思います。

まだまだ iOS 4.x をターゲットにしていきたい!という方は、
従来通りC++03以前を利用するようにしましょう。
スポンサーサイト

Xcode 4.3における xcodebuild コマンドのディレクトリパス設定

多忙なためなかなか更新できずにすみません。

メモ書き程度の内容ですが、Xcode 4.3から/Developer ディレクトリではなく
/Applications ディレクトリへインストールされるようになりました。
その関係から、 xcodebuild コマンドを実行する際にXcodeのディレクトリがないと言われてエラーになる場合があります。
なのでその場合はXcodeのディレクトリパスを再度指定しなおす必要があります。

sudo xcode-select -switch /Applications/Xcode.app


これで xcodebuild が使えるようになります。

CoreDataをRDBのように利用する Vol.1

CocoaでiPhoneアプリやMacアプリ開発をしている方なら一度は耳にする単語であるCoreData。これは主にSQLiteをラップし、データとビューのつなぎ合わせを簡潔にすることを目的としたフレームワークライブラリです。(MVCでいうところのModelに当たる)。加えて、Xcodeにおいて標準でCoreDataのエンティティ(テーブル)の作成やリレーションシップの作成をGUI上で行えるようになっているため、一見すると使いやすい印象を受けます。

ところが、現実的にこのライブラリをiPhoneアプリ開発で利用する方はあまり多くないようです。何故利用されないのかといえば、CoreDataにRDBと同じ機能を求めて利用しようとする方が多いからだと考えられます。CoreDataは前述のとおり、MVCでいうところのModelに当たる機能を担うため、ビューやコントローラとも密接に関係するように作られています。そのため、WebでいうところのHibernateやiBATIS(MyBATIS)、S2DAOといったO/Rマッピングツールとは異なり、データ単体での操作を目的としたライブラリとは少々違うものなのです。

そうなってしまうために「CoreDataなんか使えない!」となり、普通のSQLiteに走る方が多くなりがちなのです。とはいえ、Objective-Cで作られたSQLiteのO/Rマッピングツールで優れたものはまだ世に出ていないのとも事実です。FMDBという簡易的なSQLiteのラッパーライブラリは存在しますが、こちらは結局JavaでいうところのJDBCっぽい組み方が要求されるため、インピーダンスミスマッチが起こりうる可能性を抱えていますし、何よりソースコードにSQL文を記述するという時点で、普段からO/Rマッピングツールを利用している方から見れば扱いにくさを感じることも多いでしょう。

そうなると、Objective-CベースのO/Rマッピングツールをつくるしかないのか?という話になるのですが、少し待ってください。CoreDataは本当に使えないのでしょうか?そもそも、何故CoreDataは使えないという話になっているのでしょうか。その問題点さえ解決できれば、CoreDataも使えるようになる可能性があるかも知れません。

以下にCoreDataが使えない・使いにくいと言われる理由と考えられるものを挙げてみました。


01. Xcode上から初期レコードをデータベースに挿入することができない
SQLiteのよいところの1つとして、DBがファイル1つで表されているところにあります。そのため、予めレコードを追加しておき、そのファイルをコピーして利用すれば初期レコードをわざわざプログラム内で追加する必要がなくなります。SQLiteを利用している以上、CoreDataもそれが出来ないのかとおもいきや、そのように初期レコードを入れておくような仕組みはサポートされていません。ですので、結局プログラム内で初期レコードは追加する必要があります

02. 主キーや外部キーの概念がない
前述のとおり、CoreDataが目的としていることは、MVCでいうところのModelに当たります。そして、01でも記述したとおり予めレコードを追加しておくことを想定しておらず、そのアプリ上に限定してデータのCRUDが行われていることを想定しています。そのため、空っぽのDBに対してアプリ上でデータのCRUDを行う分に関しては扱いやすいのです。リレーションシップをXcode上で作成したとおりに勝手に行ってくれるばかりか、その関連先のエンティティ(NSManagedObjectクラスのインスタンスで、通常は管理オブジェクトと呼ぶ)のインスタンスも勝手に作成してくれるため、その点では非常に便利と言えます。では逆に、初期レコードを予め追加しておきたい場合はどうなのか?というと、ここで大きな問題が発生します。何故なら、主キーと外部キーの概念がないため、データの対応付けを自分で行うことが非常に難解になるからです。RDBでいうところのテーブル同士のリレーションシップには主キーと外部キーが必要です。その概念がないCoreDataに対して、どのように予めリレーションシップの存在する初期レコードを挿入することが出来るのでしょうか。まさかソースコードに1つ1つ記述していくわけには行きませんので、これは大きな課題となります

03. 追加した順番にデータを取得することが非常に面倒
主キーと外部キーがないということは、データのソートが追加順にできないことも示しています。CoreDataはソート条件を指定しないで取得した場合、追加した順番通りには取得できません。追加した順番にデータを取得したい場面は多いですよね。CoreDataでこれを行う場合、エンティティの属性に予め priority のような値属性を設けておき、順番に番号を振っておき、これを元にソートする・・・といった手段が考えられますが、CoreDataには主キーと外部キーの概念がないのと同時に、自動採番の仕組みもありません。自動採番をプログラム上で行っても、アプリを再起動したらメモリ上の自動採番用のIDはまた 0 から開始になりますので、その点からも重複を考慮し出すとどうすればいいんだ!という感じになってしまいます。(日付を利用するという手段も考えられますが、日付というのはいくらでも変更が出来るため、順番を保証する要素としては些か弱いです)

04. SQLでは1行で書ける集約関数がCoreDataだと20行くらい書かないといけない
これはもう大きな解説をするまでもない事実として紹介しておきます。SQLでいう集約関数(SUMやCOUNTといったもの)をCoreDataで扱うには、なんとソース上で20行くらい書かないといけません。データ処理を簡潔にしてソース量を減らせるのがCoreDataの売りじゃなかったのか・・・と少々疑問に感じる部分です。CoreDataは何もSQLiteだけがデータ保存先となるわけではないので、それを考えればそんなものかと考えられる節もありますが、それにしてもこの差は大きいです。



他にもまだまだあるかも知れませんが、
大きな理由としては上記の 4 つが挙げられるかと思います。

上記の問題を解決できれば確かにRDBのような使い方は出来るかと思います。とはいえ、本当に解決するだけの価値があるのかどうかが問題になりますね。結局SQLiteを直接使ったほうがいいんじゃないの?っていう疑問も当然あるかと思います。では次に、CoreDataの良い点を挙げてみます。


01. 空っぽのデータベースに対してデータをCRUDするときにリレーションも考慮してインスタンスを作成してくれる
これは前述でも紹介しましたが、CoreDataの良い点の1つです。主キー外部キーの概念がないため、その部分は勝手につなぎあわせてくれるというところです。

02. マイグレーションが簡単
アプリの機能拡張によって、データベースのスキーマが変更されることは多々あります。これをSQLiteで行う場合、非常に大変です。CoreDataでは予めバージョンごとのモデルデータを用意しておくことで、これを自動化してくれます。しかもXcode上でマイグレーションの設定が行えるので、非常に簡単です。これはSQLiteにはない優れた点ですね。

03. Key-value形式でデータを更新することが可能であり、またデータの変更に応じてコールバックを通知することが出来る
CoreDataではデータをKey-value形式で管理しているため、カラムのインデックスからデータを取得する生のSQLiteに比べるとデータが扱いやすいです。また、予めデリゲート先を指定しておくことで、データの追加や変更をコールバックとして通知することも可能です。この点もCoreDataの利点であると言えます。

04. エンティティに親子関係を作れる
これは簡単にいえば、クラスにおけるスーパークラスとサブクラスの関係と同じです。スーパークラスにあたるエンティティを親とすることで、同じ親を持ったエンティティはその親の属性を保持することが出来ます。例えば全てのエンティティに日付を持たせたい場合、各々のエンティティに日付属性を作成しなくても、親となる日付属性をもったエンティティを1つ作成し、これを継承することで全てのエンティティに日付属性を持たすことが可能になります。


以上代表的と思われる良い点を 4 つ挙げてみました。他にも、Xcodeがエンティティのクラスを自動生成してくれるところなどもあるかと思います。


この悪い点と良い点、2つを合わせて考えてみると、CoreDataにも他にはあまりない魅力的な機能が多いことがわかります。特にマイグレーションが簡単な点は大きいですし、Cocoa標準でサポートされている機能だけあって、同じくCocoa標準であるコントローラへの連携がし易い特徴もあります。

そこで、この悪い点を解決することを考えてみると、その殆どが主キーと外部キーがないことに起因するものであることがわかります。これを解決できれば、RDBのような扱い方をしつつもCoreDataを使うことも可能になると考えられます。


というわけで、次回は実際にどのようにコーディングすることで今回紹介した悪い点を解決することが出来るのか、という点についてご紹介していきます。

テーマ : iPhone
ジャンル : 携帯電話・PHS

iOS用メールライブラリを公開しました

iPhoneアプリにおけるメーラアプリは、実はあまり多くありません。(標準メーラと連携するアプリが殆どです)
これには多少なりとも理由がありまして、その1つにメール機能をサポートするiOS用ライブラリが殆ど世の中に出ていないという理由があります。有名なところでは「MailCore」というライブラリがありますが、こちらは2009年で開発がストップしているのと、日本語には全く対応していないため日本人には利用できません。
GitHubで現在も開発が続いているようです。こちらは日本語も可能なようですが、独特の使いにくさは残ったままのようです

その他の理由としては、そもそも標準メーラで満足な人が多いことや、海外ではデコメの文化もないため、あまりこのあたりを気合入れて作っている人がいないという理由もあります。

このような背景があるため、iOSでSMTPやIMAPなどを実装することは非常に難しくなっています。
とはいえ、日本においてフィーチャーフォンユーザがスマートフォンに流れてくることを考えると、デコメールの利用者も流れてくることは間違いありません。そうなると、今後標準にはないデコメ機能をサポートしたメーラの需要というのはそこそこ増えるのではと考えられます。


JavaにはJavaMailという便利なライブラリがあります。
これはメール送信・受信やMIMEパートの構築などのほとんどの機能をサポートしており、非常に高機能です。iOSでもこれが使えればな・・・と思って、今回作成したのが「FrontierMail」というライブラリです。

[FrontierMail]
https://github.com/k-kou/FrontierMail


ベースは libEtPan というライブラリを使っており、これをObjective-Cでラップして、更にJavaMailよりも簡単に利用できるように設計してあります。

まだSMTP機能とMIMEパート構築しかサポートしていないため、詳細な利用方法は上記githubにアップロードしてあるOCUnitのテストコードや、APIドキュメントを参考にしてみてください。

Xcode 4におけるiOS Frameworkの作成方法

Mac OS XではFrameworkを作成することが出来ます。
そもそもMac OS XにおけるFrameworkとは何かというと、簡単にいえば「ヘッダやリソースなどをひとまとめにし、複数のバージョンを保持することが出来るライブラリ」といったところです。

システム内において複数のアプリケーションが共有して利用できる機能をライブラリ化しておくことで、各アプリケーションがその処理を個別に実装することなく、単にそのライブラリをロードして利用するだけでよくなるというのが大きな利点でしょう。

そのため、iOSのようにシステムに自作ライブラリを追加できないOSではあまり意味が無いように思われるのですが、このFrameworkという仕組みを利用すると、ヘッダファイルやリソースを一気にまとめることが出来るので、結果的にライブラリの管理がしやすくなりますし、Xcodeプロジェクトへのインポートもこのフレームワークを1つインポートするだけでヘッダもライブラリも参照できるようになるので非常に便利です。

じゃあiOSの開発でもこれをつくろう!
と思ったのですが、いかんせんこのFrameworkの作成は上記の理由のとおり、Mac OS Xのみで推奨されていることなので、iOS向けに作成する手段をXcodeが用意していません。Xcodeで用意しているのは、Static Library(*.aファイル)の作成のみです。


となれば、自作しかありません。
1つずつポイントを抑えながら作り方を紹介していきます。


01. そもそもFrameworkとはどういうファイルなのか

Frameworkというのは実は特殊なファイル形式で1つにまとまっているわけではなく、Static Libraryやヘッダファイルを決まった形式のディレクトリ内に配置したただのディレクトリです。なので、このディレクトリにiOSで使えるStatic Library(*.aファイル)とヘッダ、リソースを配置してしまえばいいだけの話なのです。

ここまで聞くと、意外に簡単な仕組みであることが判るかと思います。
Frameworkを構成するディレクトリ名を「XXXX.framework」というディレクトリ名にしておき、その中は以下の画像のようなディレクトリ構成を作ります。
これを Framework Bundle と呼びます。
(詳細はApple公式のFramework Programming Guideを参照のこと)

Framework Bundle


02. 実機とシミュレータではアーキテクチャーが異なるためユニバーサル化が必要

iOS向けにFramework作成をXcodeがサポートしていないもう1つの理由がこれです。Mac OS X上でシミュレータでアプリを開発しているときは、i386という32bitインテルCPU上で動作します。一方、iPhone端末上ではARMとよばれるCPU上で動作します。これらは互換性のあるCPUではないため、当然ライブラリの形式も異なります。そうなると、実機向けに作成したライブラリはシミュレータ上では動かせないですし、シミュレータ向けに作成したライブラリは実機上で動かせないのです。

わざわざ切り替えて利用することも出来ないことはないですが、非常に手間のかかる作業になります。これでは、かえって開発効率の低下を招きかねません。

ではどうすればいいのかということですが、Macではこれを解消する「ユニバーサル化」という仕組みを用意しています。ユニバーサル化とは何かというと、異なるアーキテクチャーごとにビルドしたライブラリを1つのライブラリとしてまとめて利用出来るようにする仕組みです。

こうすることで、ファイルは1つだけ用意すればいいことになるので、実機上でもシミュレータ上でも動作するライブラリが完成します。その分、ファイルサイズは上昇してしまいますが、気にするほどではないでしょう。


肝心のユニバーサル化については、「lipo」コマンドで行うことが出来、以下の形式で記述します。

lipo (実機向けライブラリファイル名) (シミュレータ向けライブラリファイル名) -create -output (結合後のライブラリファイル名)


※このコマンドは後述のビルドスクリプト作成時に利用します


03. iOS向けFramework作成のためのXcodeプロジェクトの作り方

前提知識としてはここまでです。
あとは実際に作ってみたいと思います。


03-01. 以下のようにStatic Libraryプロジェクトとして作成します。

Xcode Framework 01


03-02. ターゲットの「Valid Architechtures」に「i386」を追加します

Xcode Framework 02


03-03. Packagingの「Public Headers Folder Path」の項目を「/Headers」に変更します

Xcode Framework 03


03-04. プロジェクトに「Resources」という名前でグループを作成し、その中に「Info.plist」を作成します。内容は以下を参考にしてください

Xcode Framework 04


03-05. TARGETSの下にある「Add Target」ボタンを選択し、「iOS」→「Other」→「Aggregate」を選択して「Next」ボタンを押下する。次の画面で表示されるProduct Nameは「TestFramework-Universal」といったような名前にしておく

Xcode Framework 05


03-06. すると、以下のようにターゲット「TestFramework-Universal」が追加される。ここで、その追加したターゲットの「Build Phases」を選択する

Xcode Framework 06


03-07. 「Build Phases」を選択すると、以下のように「Target Dependencies」以外何もない画面が出てくる。ここで、右下にある「Add Build Phase」→「Add Run Script」を選択する

Xcode Framework 07


03-08. すると、以下のように「Run Script」という項目が追加される

Xcode Framework 08


03-09. あとは「Shell」の部分にビルド用のスクリプトを記述する。スクリプトは以下の内容を記述する。(TestFrameworkの部分は最初から作成されているStatic Libraryのターゲットの名前を指定する)


# Environment Variables
FRAMEWORK_NAME=${PROJECT_NAME}
FRAMEWORK_VERSION=A
FRAMEWORK_VERSION_NUMBER=1.0
FRAMEWORK_BUILD_PATH="${SRCROOT}/build/${CONFIGURATION}-framework"
FRAMEWORK_DIR="${FRAMEWORK_BUILD_PATH}/${FRAMEWORK_NAME}.framework"
FRAMEWORK_PACKAGE_NAME="${FRAMEWORK_NAME}.${FRAMEWORK_VERSION_NUMBER}.zip"

# Clean directories
rm -rf "${FRAMEWORK_BUILD_PATH}"

# Build simulator and device binaries.
xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphonesimulator${IPHONEOS_DEPLOYMENT_TARGET} -target "TestFramework" -configuration ${CONFIGURATION} clean build
xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphoneos${IPHONEOS_DEPLOYMENT_TARGET} -target "TestFramework" -configuration ${CONFIGURATION} clean build

# create framework directories.
mkdir -p ${FRAMEWORK_DIR}
mkdir -p ${FRAMEWORK_DIR}/Versions
mkdir -p ${FRAMEWORK_DIR}/Versions/${FRAMEWORK_VERSION}
mkdir -p ${FRAMEWORK_DIR}/Versions/${FRAMEWORK_VERSION}/Resources
mkdir -p ${FRAMEWORK_DIR}/Versions/${FRAMEWORK_VERSION}/Headers

# create symlinks
ln -s ${FRAMEWORK_DIR}/Versions/${FRAMEWORK_VERSION} ${FRAMEWORK_DIR}/Versions/Current
ln -s ${FRAMEWORK_DIR}/Versions/Current/Headers ${FRAMEWORK_DIR}/Headers
ln -s ${FRAMEWORK_DIR}/Versions/Current/Resources ${FRAMEWORK_DIR}/Resources
ln -s ${FRAMEWORK_DIR}/Versions/Current/${FRAMEWORK_NAME} ${FRAMEWORK_DIR}/${FRAMEWORK_NAME}

# create the universal library
lipo ${SRCROOT}/build/${CONFIGURATION}-iphoneos/lib${FRAMEWORK_NAME}.a ${SRCROOT}/build/${CONFIGURATION}-iphonesimulator/lib${FRAMEWORK_NAME}.a -create -output "${FRAMEWORK_DIR}/Versions/Current/${FRAMEWORK_NAME}"

# copy files
cp ${SRCROOT}/build/${CONFIGURATION}-iphoneos/Headers/*.h ${FRAMEWORK_DIR}/Headers/
cp Info.plist ${FRAMEWORK_DIR}/Resources

# zip (配布用に TestFramework.framework ディレクトリをZIP圧縮するだけなので、配布しないなら以下はコメントアウトしてもよい)
cd ${FRAMEWORK_BUILD_PATH}
zip -ry ${FRAMEWORK_PACKAGE_NAME} $(basename $FRAMEWORK_DIR)



03-10. これでビルドできるようになったので、スキーマから「TestFramework-Universal」を選択し、ビルドを行えばFramework Bundleが作成される。ビルドを行うと、Xcodeのプロジェクトファイル(*.xcodeproj)の置いているディレクトリに「build」という名前のディレクトリが作成され、このディレクトリ内の「Debug-framework」ディレクトリ内に、「TestFramework.framework」というディレクトリが作成されている。これがFrameworkである。試しに、他のプロジェクトでこの「TestFramework.framework」ディレクトリをFrameworkとして追加できるかどうか試してみるとよい。上手くいけば、以下のようにFrameworkとして追加することが出来る

Xcode Framework 09




以上が、Xcode4におけるiOS向けFrameworkの作成方法になります。Frameworkはライブラリとして利用する場合も然ることなが、管理面や配布面でも非常に便利な形式ですので是非とも活用していきましょう。
プロフィール

Author:Kou
モバイル関連の開発ばかりやってる人のブログです。たまにWebもやります。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。