Xcode には「Objective-C 2.0 に変換...」という機能がある。これは古いコードを Objective-C 2.0 から導入された Fast Enumerator と Property に変換(追加)してくれるというもの。
例えば次のクラスを定義しておいて Xcodeのメニュー「編集」から「Objective-C 2.0 に変換...」を選ぶ。
ダイアログが開くので「プロパティを使用」にチェックを付けて「プレビュー」を押す。
すると候補が表示される。必要なものだけチェックして「適用」を押すと。
こうなる。*.hファイルに @property が追加され
*.m ファイルにも @synthesize が追加される。
メンバ変数を後で追加した場合に適用した場合、その追加したメンバ変数のみ @property/@synthesize を追加してくれる。
変換対象がない場合はエラー。
適用前にファイルを選択するとどのような変更が加えられるかをプレビューできる。
iOS向け開発の場合、定番の "(nonatomic, retain)" を後から加える必要があったりするのだが、自動生成されるのは役に立つ。
もうひとつの「ループを改新」は NSEnumerator を使っている箇所を Fast Enumeration(for in〜)に変更してくれる。
最近は最初から Fast Enumeration を使うのでこちらの出番は無いだろう。
[iOS] Static Library (5) Frameworkを作成する
2010年11月19日金曜日 | Published in iOS 4.0, iPhone, Xcode 3.2.3, サンプルあり, ライブラリ開発 | 0 コメント
[前回] Cocoaの日々: [iOS] Static Library (4) Universal Static Library
今回は iOS 向けに Framework を作成する。
前回のエントリでまとめたのでそちらをどうぞ。
Cocoaの日々: [Mac][iOS] Frameworkとは?
今回は SampleKit という Framework を作成する。
中身はこんな感じ。
Xcodeプロジェクトはこんな感じになる。
Frameworkを作成した後、動作確認の為にサンプルアプリケーションを作る。
Framework 作成手順
まずは Xcode を開き、テンプレートから "Cocoa Touch Static Library" を選択する。名前は "SampleKit" とした。
こんなプロジェクトが生成される。
Resources グループを作成し、Info.plist ファイルを作成する。
Info.plist の中身はこんな感じ。
上記はMac OS X 用フレームワーク作成テンプレートのものをコピーして、それにドキュメントで推奨されていた項目を付け足した( Copyright, Get Info stringなど)。Bundle Versions string, short と Bundle name はこの後のシェルスクリプトで参照する。
公開ヘッダ用のディレクトリを準備する。まず Xcodeで新規にグループを作成し "Headers" と名前をつける。
ただこれは Xcodeプロジェクト上だけで有効なもので自分でディレクトリを作成してひもづける必要がある。"Headers"の情報を開き、パスの「選択」を選ぶ。
ダイアログが開いたら左下の「新規フォルダ」ボタンを押して "Headers" というディレクトリを作成する。
このディレクトリを選択すると情報のパスの欄に "Headers" と表示される。
この Headersディレクトリに入っているヘッダファイル(*.h)を後ほど Frameworkディレクトリへコピーする。公開したくないヘッダファイルがある場合はここへ入れなければ良い。
動作確認用のクラスのソースコードファイルを Classes 配下へ追加する。
今回 SampleClass.h は公開したい(Frameworkに同梱)ので Headers 配下に配置しておく逆に公開したくない場合は Classesなどへ入れておく。
中身はこんな感じ。
Framework作成用に新規ターゲットを作成する。ターゲットの上で右クリックして「追加」→「新規ターゲット」→ "Shell Script Target" を選択する。
ターゲット名を "SampleKit.framework" とする。
ターゲット内の「スクリプトを実行」を開き、ビルド用のシェルスクリプトを記述する。
ビルド用シェルスクリプト
Xcode 4.1 から環境変数 BUILD_STYLE が使われなくなった。代わりに CONFIGURATION を使う。上記スクリプト内の ${BUILD_STYLE} をすべて ${CONFIGURATION} で置換する。
xcodebuild コマンドを使い実機(iphoneos)とシミュレータ(iphonesimulator)用のバイナリ(*.a) ファイルを作成する。その後、lipo コマンドで1つにまとめ(ユニバーサル化)Frameworkのディレクトリを構成する。最後はそのディレクトリを ZIP圧縮する。
ビルドしてみよう。"SampleKit.framework" を選択しビルドを実行する。
※Device/Simulator は無視される。Debug/Release は有効。
フォルダを確認すると...
できた。
file コマンドでバイナリを確認してみる。
作成した Framework を使うサンプルを作ってみる。View-basedなプロジェクトを作成し、先ほど作成した Frameworkを追加する。
Frameworkとして認識された。
Xcodeのウィンドウの左ツリーで SampleKit.frameworkを選択すると右側には内包している SampleClass.h が表示される。
コントローラに下記コードを追加する。
シミュレータで実行すると...
"Frameworkとは?" の記事で触れた様に iOS の場合は Framework は静的ライブラリのプレースホルダとして使うのが一般的と思われる。
運用イメージ
この為、もし自作した Framework がリソース(nib,画像など)を含む場合は、利用側のプロジェクトのビルドフェーズでそれらをアプリケーションバンドル内へコピーして一緒に配布する必要がある。
なお仕組み自体は動的ライブラリの格納も可能なので、"Frameworkとは?"で取り上げた embed形態を取ることで実行時にリンクさせることもできる。この場合だと実行時に必要なライブラリしか読み込まれない為、フットプリント(メモリ消費)を減らせる可能性がある。
スクリプト内のPATH環境変数設定で古い SDKのディレクトリを指定している場合など。
export PATH=/Developer/usr/bin:"$PATH"
明示的に指定する必要がなければ、この PATH設定を削除しておく。すると現在実行中の Xcodeの SDKのバージョンが使用される
ターゲットが見つからない場合などは、スクリプト内の xcodebuild コマンドで指定しているターゲットが正しく設定されていない可能性がある。
スクリプト内の xcodebuildコマンドのターゲットに指定しているのは、最初にテンプレートで作成されたターゲットである "SampleKit" である。このターゲット名を用意するのに Info.plist 内の "Bundle Name" を使用している。この名前とライブラリ(*.a)作成用のターゲット名が一致している必要がある。
シェルスクリプト内の該当箇所は下記になる。
Headers ディレクトリにファイルが無いとこのエラーが出る。例えば追加したヘッダファイルのパスが間違っていた場合など。
この場合は該当するファイルが入っているフォルダを開く。
そしてファイルを Headers へコピーする。
すると Xcode 上でファイルが赤くなる。これは指定パス位置にファイルが無いことを示している。
そこで情報を開き、パスの「選択」ボタンを押して目的のファイルを選択する。
これでビルドが通る様になる。
GitHubからどうぞ。
Framework作成プロジェクト
SampleKit at 2010-11-19c from xcatsan's iOS-Sample-Code - GitHub
Frameworkを使うサンプル
SampleKitClient at 2010-11-19 from xcatsan's iOS-Sample-Code - GitHub
※ SampleKitClient 内で参照している SampleKit へのパスは私のPCのローカル環境になっています。ビルドする際には SampleKit を別途ダウンロードして、参照先をそちらに切り替えて下さい。
lipo(1) Mac OS X Manual Page
複数のアーキテクチャ向けのライブラリやアプリケーションバイナリを1つにまとめることができるツール。
[参考情報]
Undocumented Mac OS X:第13回 Universal Binary【後編】 (2/4) - ITmedia エンタープライズ
yebo blog: ユニバーサルバイナリ化するツール lipo
PlistBuddy(8) Mac OS X Manual Page
*.plist ファイルの値を設定したり取得したりできるコマンド。/usr/libexec/配下に存在する。
PlistBuddy を使ってバージョン番号を自動的にインクリメントするアイディアも公開されている。
Incrementing Build Numbers in Xcode | Dave DeLong
今回 PlistBuddy を使い、シェルスクリプトから Info.plist 内の情報を取り出してビルドに使用した。本来であればビルド設定の値(環境変数に設定される)を使うべきで、一般的な Xcodeプロジェクトではそのようになっている。Info.plist 自体にも環境変数が埋めこまれていてビルド時にこれが置換される。下記は Mac OS X 用 Framework テンプレートで生成された Info.plist。
${EXECUTABLE_NAME} など環境変数が使われているのがわかる。Info.plist 内の環境変数の置換はビルド時に行われている。以下は Mac OS X 用Framework をビルドしている時のメッセージ。
メッセージに表示されている builtin-infoPlistUtility は外部コマンドとして存在するものではなくて、Xcode内部の機能として存在するようだ。先頭にある "ProcessInfoPlistFile" はビルドのルール設定項目と思われるが見つからなかった(図にあるように CopyPlistFile というルールは設定項目にある)。
また今回使用しているターゲットタイプ(Shell Script)ではルールが編集できない。
仕方ないので
iOS向けの Framework に関しては Appleからドキュメントが提供されていない。この為、多くの人が自分たちで試行錯誤して Framework作成を試みている。そのおかげもあって Framework作成はだいたいすんなり行うことができた。情報提供してくれた方々にはこの場を借りてお礼を申し上げます。
iPhone アプリ開発で使える Framework の作り方 | KRAY Inc
Framework の作成方法が図入りで丁寧に解説されている。とても参考になった。
[Mac][iPhone][develop] iPhone OS用のほぼFrameworkの作り方 - Ni chicha, ni limona -平均から抜けられない僕-
ライブラリファイル(*.a)を lipo を使ってユニバーサル化する方法の紹介。
Mac♪Mac♪Mac♪ - 第11回 フレームワークを作成する
Mac OS X でフレームワークを作成する解説。
How to (almost) create your own iPhone OS framework
Making Your Own iPhone Frameworks @ Cocoanetics
“Making Your Own iPhone Frameworks”
今回は iOS 向けに Framework を作成する。
Frameworkとは?
前回のエントリでまとめたのでそちらをどうぞ。
Cocoaの日々: [Mac][iOS] Frameworkとは?
サマリー
今回は SampleKit という Framework を作成する。
中身はこんな感じ。
Xcodeプロジェクトはこんな感じになる。
ターゲットを2つを用意する。 Frameworkを作成するターゲット(上図の SampleKit.framework ターゲット)を使いビルドを開始すると、最初に SampleKit ターゲットを呼び出して各アーキテクチャ毎のライブラリ(*.a)を作成する。
そしてこれらを lipoコマンドでユニバーサル化した後、Frameworkのディレクトリを構成する。最後に ZIP圧縮して配布できるようにする。
Frameworkで公開したいヘッダファイル(*.h)はHeaders に入れておく。これらがビルド時に Framework/Headers へコピーされる。公開したくないヘッダファイルはここへは入れない(Classesなどへ入れる)。
ビルド時に使用する Framework名称とバージョン番号(ZIPファイル名に使用)は Info.plist ファイル内の値を使う[*1]。
[*1] ビルド設定(環境変数)は使わない
Frameworkを作成した後、動作確認の為にサンプルアプリケーションを作る。
Framework 作成手順
1. Xcode プロジェクト作成
まずは Xcode を開き、テンプレートから "Cocoa Touch Static Library" を選択する。名前は "SampleKit" とした。
こんなプロジェクトが生成される。
2. リソース準備
Resources グループを作成し、Info.plist ファイルを作成する。
Info.plist の中身はこんな感じ。
上記はMac OS X 用フレームワーク作成テンプレートのものをコピーして、それにドキュメントで推奨されていた項目を付け足した( Copyright, Get Info stringなど)。Bundle Versions string, short と Bundle name はこの後のシェルスクリプトで参照する。
3. ヘッダ用ディレクトリ準備
公開ヘッダ用のディレクトリを準備する。まず Xcodeで新規にグループを作成し "Headers" と名前をつける。
ただこれは Xcodeプロジェクト上だけで有効なもので自分でディレクトリを作成してひもづける必要がある。"Headers"の情報を開き、パスの「選択」を選ぶ。
ダイアログが開いたら左下の「新規フォルダ」ボタンを押して "Headers" というディレクトリを作成する。
このディレクトリを選択すると情報のパスの欄に "Headers" と表示される。
この Headersディレクトリに入っているヘッダファイル(*.h)を後ほど Frameworkディレクトリへコピーする。公開したくないヘッダファイルがある場合はここへ入れなければ良い。
4. ソースコード準備
動作確認用のクラスのソースコードファイルを Classes 配下へ追加する。
今回 SampleClass.h は公開したい(Frameworkに同梱)ので Headers 配下に配置しておく逆に公開したくない場合は Classesなどへ入れておく。
中身はこんな感じ。
@interface SampleClass : NSObject { } - (NSString*)helloString; @end
@implementation SampleClass - (NSString*)stringHello { return @"Hello mac!"; } @end
5. ターゲット準備
Framework作成用に新規ターゲットを作成する。ターゲットの上で右クリックして「追加」→「新規ターゲット」→ "Shell Script Target" を選択する。
ターゲット名を "SampleKit.framework" とする。
ターゲット内の「スクリプトを実行」を開き、ビルド用のシェルスクリプトを記述する。
ビルド用シェルスクリプト
#-------------------------------------------------------------------- echo "[0] Framework: Preparing ..." #-------------------------------------------------------------------- FRAMEWORK_NAME=$(/usr/libexec/PlistBuddy -c "Print CFBundleName" Info.plist) BUILD_TARGET_NAME=$FRAMEWORK_NAME FRAMEWORK_VERSION_NUMBER=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" Info.plist) FRAMEWORK_VERSION=A FRAMEWORK_BUILD_PATH="build/${BUILD_STYLE}-framework" FRAMEWORK_DIR="${FRAMEWORK_BUILD_PATH}/${FRAMEWORK_NAME}.framework" PACKAGENAME="${FRAMEWORK_NAME}.${FRAMEWORK_VERSION_NUMBER}.zip" #-------------------------------------------------------------------- echo "[1] Framework: Building libraries ..." #-------------------------------------------------------------------- xcodebuild -configuration ${BUILD_STYLE} -target ${BUILD_TARGET_NAME} clean xcodebuild -configuration ${BUILD_STYLE} -target ${BUILD_TARGET_NAME} \ -sdk iphonesimulator${IPHONEOS_DEPLOYMENT_TARGET} [ $? != 0 ] && exit 1 xcodebuild -configuration ${BUILD_STYLE} -target ${BUILD_TARGET_NAME} \ -sdk iphoneos${IPHONEOS_DEPLOYMENT_TARGET} [ $? != 0 ] && exit 1 echo "Framework: Cleaning framework..." [ -d "${FRAMEWORK_BUILD_PATH}" ] && rm -rf "${FRAMEWORK_BUILD_PATH}" #-------------------------------------------------------------------- echo "[2] Framework: Setting up 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 #-------------------------------------------------------------------- echo "[3] Framework: Creating symlinks..." #-------------------------------------------------------------------- ln -s ${FRAMEWORK_VERSION} ${FRAMEWORK_DIR}/Versions/Current ln -s Versions/Current/Headers ${FRAMEWORK_DIR}/Headers ln -s Versions/Current/Resources ${FRAMEWORK_DIR}/Resources ln -s Versions/Current/${FRAMEWORK_NAME} ${FRAMEWORK_DIR}/${FRAMEWORK_NAME} #-------------------------------------------------------------------- echo "[4] Framework: Creating library..." #-------------------------------------------------------------------- lipo -create \ build/${BUILD_STYLE}-iphoneos/lib${FRAMEWORK_NAME}.a \ build/${BUILD_STYLE}-iphonesimulator/lib${FRAMEWORK_NAME}.a \ -o "${FRAMEWORK_DIR}/Versions/Current/${FRAMEWORK_NAME}" #-------------------------------------------------------------------- echo "[5] Framework: Copying assets into current version..." #-------------------------------------------------------------------- cp Headers/*.h ${FRAMEWORK_DIR}/Headers/ cp Info.plist ${FRAMEWORK_DIR}/Resources/ #-------------------------------------------------------------------- echo "[6] Framework: Packaging framework..." #-------------------------------------------------------------------- cd ${FRAMEWORK_BUILD_PATH} zip -ry ${PACKAGENAME} $(basename $FRAMEWORK_DIR)(2011-09-05追記)
Xcode 4.1 から環境変数 BUILD_STYLE が使われなくなった。代わりに CONFIGURATION を使う。上記スクリプト内の ${BUILD_STYLE} をすべて ${CONFIGURATION} で置換する。
(2011-10-21追記)
Xcode 4.2 から SDK 5.0 のみの提供となった。この為、ターゲットを 4.3 としてビルドするとエラーになる。その場合は xcodebuild コマンドの引数から ${IPHONEOS_DEPLOYMENT_TARGET} を取り除く。
(修正前)xcodebuild -configuration ${BUILD_STYLE} -target ${BUILD_TARGET_NAME} \ -sdk iphoneos${IPHONEOS_DEPLOYMENT_TARGET} (修正後)xcodebuild -configuration ${BUILD_STYLE} -target ${BUILD_TARGET_NAME} \ -sdk iphoneos
(2011-10-21追記)
Xcode 4.2 でターゲットを 4.3 としてビルドするとエラーが出る場合がある。この場合はコンパイラを Apple LLVM compiler 3.0 から LLVM GCC 4.2 に切り替えると解消する(かもしれない)。
xcodebuild コマンドを使い実機(iphoneos)とシミュレータ(iphonesimulator)用のバイナリ(*.a) ファイルを作成する。その後、lipo コマンドで1つにまとめ(ユニバーサル化)Frameworkのディレクトリを構成する。最後はそのディレクトリを ZIP圧縮する。
6. ビルド
ビルドしてみよう。"SampleKit.framework" を選択しビルドを実行する。
※Device/Simulator は無視される。Debug/Release は有効。
フォルダを確認すると...
できた。
file コマンドでバイナリを確認してみる。
$ file SampleKit SampleKit: Mach-O universal binary with 3 architectures SampleKit (for architecture armv6): current ar archive random library SampleKit (for architecture armv7): current ar archive random library SampleKit (for architecture i386): current ar archive random libraryちゃんと arm6/7、i386用のライブラリが入っている。
Frameworkを利用するサンプル
作成した Framework を使うサンプルを作ってみる。View-basedなプロジェクトを作成し、先ほど作成した Frameworkを追加する。
Frameworkとして認識された。
Xcodeのウィンドウの左ツリーで SampleKit.frameworkを選択すると右側には内包している SampleClass.h が表示される。
コントローラに下記コードを追加する。
#import <SampleKit/SampleClass.h> : - (void)viewDidLoad { [super viewDidLoad]; SampleClass* obj = [[[SampleClass alloc] init] autorelease]; NSLog(@"%@", [obj stringHello]); }Framework 内のヘッダファイルは下記の形式でインポートする。
#import <フレームワーク名/ヘッダファイル名>
シミュレータで実行すると...
SampleKitClient[19559:207] Hello mac!デバッグコンソールに出た。その後、実機でも動作確認ができた。良さそうだ。
考察
"Frameworkとは?" の記事で触れた様に iOS の場合は Framework は静的ライブラリのプレースホルダとして使うのが一般的と思われる。
運用イメージ
この為、もし自作した Framework がリソース(nib,画像など)を含む場合は、利用側のプロジェクトのビルドフェーズでそれらをアプリケーションバンドル内へコピーして一緒に配布する必要がある。
なお仕組み自体は動的ライブラリの格納も可能なので、"Frameworkとは?"で取り上げた embed形態を取ることで実行時にリンクさせることもできる。この場合だと実行時に必要なライブラリしか読み込まれない為、フットプリント(メモリ消費)を減らせる可能性がある。
トラブルシュート
SDKが見つからないエラーが出た場合
スクリプト内のPATH環境変数設定で古い SDKのディレクトリを指定している場合など。
export PATH=/Developer/usr/bin:"$PATH"
明示的に指定する必要がなければ、この PATH設定を削除しておく。すると現在実行中の Xcodeの SDKのバージョンが使用される
ビルド中にエラー
ターゲットが見つからない場合などは、スクリプト内の xcodebuild コマンドで指定しているターゲットが正しく設定されていない可能性がある。
スクリプト内の xcodebuildコマンドのターゲットに指定しているのは、最初にテンプレートで作成されたターゲットである "SampleKit" である。このターゲット名を用意するのに Info.plist 内の "Bundle Name" を使用している。この名前とライブラリ(*.a)作成用のターゲット名が一致している必要がある。
シェルスクリプト内の該当箇所は下記になる。
#-------------------------------------------------------------------- echo "[0] Framework: Preparing ..." #-------------------------------------------------------------------- FRAMEWORK_NAME=$(/usr/libexec/PlistBuddy -c "Print CFBundleName" Info.plist) // ←これ BUILD_TARGET_NAME=$FRAMEWORK_NAME // ←
ヘッダが無いというエラーが出る
Headers ディレクトリにファイルが無いとこのエラーが出る。例えば追加したヘッダファイルのパスが間違っていた場合など。
この場合は該当するファイルが入っているフォルダを開く。
そしてファイルを Headers へコピーする。
すると Xcode 上でファイルが赤くなる。これは指定パス位置にファイルが無いことを示している。
そこで情報を開き、パスの「選択」ボタンを押して目的のファイルを選択する。
これでビルドが通る様になる。
ソースコード
GitHubからどうぞ。
Framework作成プロジェクト
SampleKit at 2010-11-19c from xcatsan's iOS-Sample-Code - GitHub
Frameworkを使うサンプル
SampleKitClient at 2010-11-19 from xcatsan's iOS-Sample-Code - GitHub
※ SampleKitClient 内で参照している SampleKit へのパスは私のPCのローカル環境になっています。ビルドする際には SampleKit を別途ダウンロードして、参照先をそちらに切り替えて下さい。
備考:コマンド
lipo
lipo(1) Mac OS X Manual Page
複数のアーキテクチャ向けのライブラリやアプリケーションバイナリを1つにまとめることができるツール。
[参考情報]
Undocumented Mac OS X:第13回 Universal Binary【後編】 (2/4) - ITmedia エンタープライズ
yebo blog: ユニバーサルバイナリ化するツール lipo
PlistBuddy(8) Mac OS X Manual Page
PlistBuddy
*.plist ファイルの値を設定したり取得したりできるコマンド。/usr/libexec/配下に存在する。
# CFBundleName 取得例 FRAMEWORK_NAME=$(/usr/libexec/PlistBuddy -c "Print CFBundleName" Info.plist)
PlistBuddy を使ってバージョン番号を自動的にインクリメントするアイディアも公開されている。
Incrementing Build Numbers in Xcode | Dave DeLong
備考
今回 PlistBuddy を使い、シェルスクリプトから Info.plist 内の情報を取り出してビルドに使用した。本来であればビルド設定の値(環境変数に設定される)を使うべきで、一般的な Xcodeプロジェクトではそのようになっている。Info.plist 自体にも環境変数が埋めこまれていてビルド時にこれが置換される。下記は Mac OS X 用 Framework テンプレートで生成された Info.plist。
${EXECUTABLE_NAME} など環境変数が使われているのがわかる。Info.plist 内の環境変数の置換はビルド時に行われている。以下は Mac OS X 用Framework をビルドしている時のメッセージ。
メッセージに表示されている builtin-infoPlistUtility は外部コマンドとして存在するものではなくて、Xcode内部の機能として存在するようだ。先頭にある "ProcessInfoPlistFile" はビルドのルール設定項目と思われるが見つからなかった(図にあるように CopyPlistFile というルールは設定項目にある)。
また今回使用しているターゲットタイプ(Shell Script)ではルールが編集できない。
仕方ないので
Info.plist ==(参照)==> 環境変数 シェルスクリプト ==(参照)==> 環境変数をやめて
Info.plist をオリジナル シェルスクリプト ==(参照)==> Info.plistとした。
Framework 参考情報
iOS向けの Framework に関しては Appleからドキュメントが提供されていない。この為、多くの人が自分たちで試行錯誤して Framework作成を試みている。そのおかげもあって Framework作成はだいたいすんなり行うことができた。情報提供してくれた方々にはこの場を借りてお礼を申し上げます。
iPhone アプリ開発で使える Framework の作り方 | KRAY Inc
Framework の作成方法が図入りで丁寧に解説されている。とても参考になった。
[Mac][iPhone][develop] iPhone OS用のほぼFrameworkの作り方 - Ni chicha, ni limona -平均から抜けられない僕-
ライブラリファイル(*.a)を lipo を使ってユニバーサル化する方法の紹介。
Mac♪Mac♪Mac♪ - 第11回 フレームワークを作成する
Mac OS X でフレームワークを作成する解説。
How to (almost) create your own iPhone OS framework
Making Your Own iPhone Frameworks @ Cocoanetics
“Making Your Own iPhone Frameworks”
[Mac][iOS] Frameworkとは?
2010年11月18日木曜日 | Published in iOS 4.0, Mac OS X 10.6, ライブラリ開発 | 0 コメント
Mac OS X / iOS における Frameworkの作成や利用方法について調べてみた。Framework に関しては Mac OS X 向けに下記のリファレンスが用意されている。
Framework Programming Guide: Introduction to Framework Programming Guide
この内容を読みつつ調べたことを加味して Frameworkについてまとめた。
なお iOS においては Framework の仕組みはシステムでサポートされているものの、ユーザが独自の Framework を作成することは推奨されていない(ドキュメントが存在しない)。また iOSでは開発時にユーザが独自の Frameworkを作成できるものの実機への配置はできない[*1]。この為、その特徴であるコード共有や動的リンク、バージョン管理などのFrameworkを使う上での様々なメリットは Mac OS X のみで有効であり、iOSにおいてはその恩恵を受けることができない。
[*1] embedなら可能かもしれない。
今回は長いので目次を用意した。
Framework Programming Guide: What are Frameworks? によれば
(iOS開発時の)ライブラリ運用の観点からすると、ヘッダファイルをライブラリ(*.a)とひとまとめに配布できるので使い勝手がいい。例えば開発で組み込む場合、前回までの *.a 形態だと他にヘッダファイルも別に配置しなければならない。ファイルが多くなるとこの作業は煩雑になる。また nib や imageファイル他、何でも格納できるのでこれらリソースファイルを使うライブラリの管理も容易に扱える。ローカライズも可能なので1つのライブラリで複数言語にも対応が可能。
また Framework 内のライブラリは複数のアプリケーションから共有して利用することができる(動的ライブラリを使用した場合)。
[*1] ライブラリを含まないリソースだけの Frameworkの構成も可能(例えば画像だけを含むFrameworkなど)
[*2] 通常のディレクトリなので Finderで普通に閲覧ができる(*.appのようなパッケージ形態ではない)
Frameworkの構成はドキュメントに解説がある。
Framework Programming Guide: Anatomy of Framework Bundles
Frameworkを構成するディレクトリのことを "Framework Bundles" と呼ぶ。上記から図を引用していくつかの構成パターンを見てみよう。
2つのバージョン(AとB)を含み、Versions/Current のシンボリックリンク先でバージョンの切り替えを行っている。
このタイプは "versioned bundle" とも呼ばれ、1つの Framework Bundlesでアプリケーション毎に異なるバージョンのライブラリを利用することができる。
複数バージョンに加えて、それぞれで英語用のドキュメントリソースを含んでいる。
Framework Bundles 内に用意できる標準的なディレクトリは次の通り。
Framework Bundles 内には通常の Bundleと同様 Info.plist を設置する。
Framework Programming Guide: Anatomy of Framework Bundles より転載。
以下は CoreLocation.framwork の Info.plist の例
Framwork Bundles では複数のバージョンを管理できる。
上の例では MyFramework が A,B,C の3つのバージョンを格納しおり、アプリケーションがそれぞれに適したバージョンのライブラリを利用している。
対象は動的ライブラリのみとなる。iOS の場合、動的ライブラリが推奨されない上に、独自で作成したFrameworkを配置して複数アプリケーション(プロセス)間で共有することは行えないので実質的な利用は Mac OS X のみに限られる。
バージョン管理には "Major Versions" と "Minor Versions" の2種類のタイプを使い分ける。
Major Versionを変えるケースとして次の項目が挙げられている。
Minor Verions を変更するケースとして次の項目が挙げられている。
CoreLocation.framework を例に実際の構成を見てみる。まずは iPhoneOS4.2.sdk の場合。
Japanese.lproj 内には InfoPlist.strings と locationd.strings が含まれる。
同じ CoreLocation.framework について Mac OS X 10.6 用の Frameworkを見てみる。
格納されているアーキテクチャはこんな感じ。
上記までは開発用のSDKに含まれる Frameworkを見ていた。今度は Mac OS X 10.6 のシステムで実際に使われている Frameworkを見てみる。
その他、Resources フォルダには画像ファイルなどもファイルを収めることができる。下記は QuickLook.framwork の Resources 配下。
[参考情報] Undocumented Mac OS X:第13回 Universal Binary【後編】 (3/4) - ITmedia エンタープライズ
lipo や file コマンドの使い方など。
複数の Frameworkをグループ化してひとつにまとめたものを "Umbrella Framework" と呼ぶ。基本的には通常の Framework と同じ構成を持つが、次の特徴を持つ。
1. 対象となるFrameworkのヘッダファイルを含む
2. 対象となるFrameworkを格納する(Frameworks ディレクトリ)
Mac OS X の場合、Core Services がこれに当たる。CoreServices.framework はこんな感じ。
複数の Framework がFrameworks に格納されているのがわかる。Headers には下記のファイルが格納されている。
CoreServices.h の中身を見ると各 Frameworkのヘッダファイルを読み込んでいるのがわかる。
なお独自の Umbrella Framework 作成は推奨されていない。
Framework Programming Guide: Guidelines for Creating Frameworks - "Dont' Create Umbrella Frameworks"
※iOS アプリでは動的共有ライブラリは推奨されないので、ここの話は Mac OS X 向けとなる。
Framework 内の動的ライブラリは実行時にそれを必要とするアプリケーションプログラムとリンクされる。Frameworkはメモリ上に1つのみ存在して、それを必要とするすべてのライブラリで共有される。またリンクはアプリケーションが必要とする Framework 内のモジュール(*.o)単位で行われる。
Framework Programming Guide: Frameworks and Binding より
関数 a() が main() 内で呼ばれる時に a.o がリンクされる。同様に関数 b() が doThat() 内で呼ばれるときに b.o がリンクされる。c.o は c() が呼ばれないのでリンクされることは無い。
リンク時にはバージョンチェックが行われ、アプリケーションプログラムが必要とするバージョンをFrameworkがサポートしていない場合、そのアプリケーションプログラムは強制終了する。
その他、Framework は Prebinding(実行時リンクにかかる時間を短縮するためあらかじめリンクに必要な前処理を施しておく)もサポートする。詳しくは下記を参照のこと。
Framework Programming Guide: Frameworks and Binding - Framework and Prebinding
※これは Framework を作る側ではなく、利用する側の話
新しいバージョンのライブラリで提供する機能をアプリケーションプログラムが利用した場合、このアプリケーションプログラムを古いバージョンしか持たない環境で実行した場合クラッシュする。
Weak Linking を使いシンボル(関数やメソッド)をリンクすると、実行時にそのシンボルが存在しなくてもアプリケーションプログラムの実行を継続することができる(ただしそのシンボルを利用=関数呼び出しなど=しようとするとクラッシュする)。この場合は該当するシンボルのアドレスが NULL かどうかチェックし、非NULLの時のみ利用するようにする。Weak Linking を使うには関数のプロトタイプ宣言に weak_import 属性を指定する。
あるいはターゲットのビルド設定内の「他のリンクフラグ」に下記のコマンドラインオプションを追加する。
[参考情報] TN 2064:Ensuring Backwards Binary Compatibility - Weak Linking and Availability Macros on Mac OS X
Weak Linking の日本語解説。a)起動できない、(b)実行中にクラッシュする、(c)正しく実行するが旧版の Mac OS X では事前バインドできない、といったケースについて詳しい解説と、それらの解決策としての Weak Linking についての説明がされている。
[参考情報] iOS 4.0 と iPhone OS 3.x の両方で動作するアプリケーションをビルドする設定 - 24/7 twenty-four seven
Xcode での Weak Linking 設定の仕方など。
クラス名の先頭に Framework名に関係した2文字のアルファベットを付与する。
(例)Core Graphics → "CG"
既存クラスへカテゴリを追加する場合、メソッド名称の先頭にも同様に2文字のアルファベットを付ける。
Framework Programming Guide: Creating a Framework
Mac OS X 10.4以降では Framework の初期化と終了処理として "Module Initializers" と "Module Finalizers" が利用できる。
デフォルトの動作として Xcodeのリンカは Framework内のすべてのシンボルをエクスポートする。実行時には Dynamic Link editor が Frameworkでエクスポートされているすべてのシンボルを読み込む。この事は外部に公開する必要の無い内部的な関数やメソッドも対象となることを意味する。この為、シンボル数が多くなりロード時間の増加、所要メモリの増加といった問題が生じるケースもある。これを避けるためにXcodeでは公開するシンボルを明示的に指定してリンクさせることができるオプションが用意されている。手順は次のとおり。
1. エクスポートするシンボルを書きだしたテキストファイル(*.txt)を用意する。
記述するシンボル名は nm コマンドで一覧したものを使う。
2. 作成したテキストファイルをプロジェクトの Resources へ追加する。
3. ターゲットの情報を開きビルドタブ内の「書きだされるシンボルのファイル」にこのファイル名を指定する。
逆に一部のシンボルのみ公開したくない場合はその一覧が記載されたテキストファイルを作成し「書き出されないシンボルのファイル」に指定する。
[参考情報] nm(1) Mac OS X Developer Tools Manual Page
Framework の配置場所は /Library/Frameworks とそれ以外の2つに分けられる。
Framework の開発から配布・利用までの運用パターンを考えてみる。
アプリケーションとは別に実行環境へ Framework をインストールする必要がある。メリットとしては1つの Framework を複数のアプリケーション(プロセス)で共有することができる。
この場合、実行環境に Frameworkがインストールされている必要は無い。メリットは、配布が簡単で確実に実行できること。
iOS の場合、Mac OS X のように実機の /Library/Frameworks などへ動的ライブラリを配置することはできない。この為、開発時のみに使う静的ライブラリとヘッダファイルのプレースホルダ的な使い方となる。
Software Delivery Guide: Introduction
ソフトウェア配布のガイド。Frameworkの配布方法についても触れられている。
Launch Time Performance Guidelines: Introduction to Launch Time Performance Guidelines
ソフトウェア起動時間に関するガイド。Frameworkにおける Tipsも記載されている。
Framework Programming Guide: Introduction to Framework Programming Guide
この内容を読みつつ調べたことを加味して Frameworkについてまとめた。
なお iOS においては Framework の仕組みはシステムでサポートされているものの、ユーザが独自の Framework を作成することは推奨されていない(ドキュメントが存在しない)。また iOSでは開発時にユーザが独自の Frameworkを作成できるものの実機への配置はできない[*1]。この為、その特徴であるコード共有や動的リンク、バージョン管理などのFrameworkを使う上での様々なメリットは Mac OS X のみで有効であり、iOSにおいてはその恩恵を受けることができない。
[*1] embedなら可能かもしれない。
目次
今回は長いので目次を用意した。
- Frameworkとは?
- 特徴
- 構成パターン
- ディレクトリ
- Info.plist
- バージョン管理
- iOS や MacOSX における実際の構成例
- Umbrella Framework
- 実行時リンク
- Weak Linking
- Frameworkの作成
- Frameworkの初期化
- シンボルのエクスポート
- Frameworkの配置
- 運用モデル
- 関連情報
1. Frameworkとは?
Framework Programming Guide: What are Frameworks? によれば
A framework is a hierarchical directory that encapsulates shared resources, such as a dynamic shared library, nib files, image files, localized strings, header files, and reference documentation in a single package.「Frameworkとは、動的共有ライブラリや nibファイル、imageファイル、ローカライズファイル、ヘッダファイル、ドキュメント等のリソースファイルを1つのパッケージにまとめたディレクトリ」を指すとのこと。
(iOS開発時の)ライブラリ運用の観点からすると、ヘッダファイルをライブラリ(*.a)とひとまとめに配布できるので使い勝手がいい。例えば開発で組み込む場合、前回までの *.a 形態だと他にヘッダファイルも別に配置しなければならない。ファイルが多くなるとこの作業は煩雑になる。また nib や imageファイル他、何でも格納できるのでこれらリソースファイルを使うライブラリの管理も容易に扱える。ローカライズも可能なので1つのライブラリで複数言語にも対応が可能。
また Framework 内のライブラリは複数のアプリケーションから共有して利用することができる(動的ライブラリを使用した場合)。
2. 特徴
- 静的・動的ライブラリの他、nibファイル、imageファイル、ローカライズファイル、ヘッダファイル、ドキュメントを1つにまとめることができる [*1]
- ディレクトリ構成は Core Foundation/Cocoa でサポートされる bundleの形式を取る [*2]
- 複数のバージョンをサポート
- Frameworkは読み取り専用のリソースとしてメモリ内に配置され、他のプロセスと共有することでメモリのフットプリントを低減できる(Mac OS X)
- 複数のFramework をグループ化することができる( "umbrella frameworks" と呼ばれる)
[*1] ライブラリを含まないリソースだけの Frameworkの構成も可能(例えば画像だけを含むFrameworkなど)
[*2] 通常のディレクトリなので Finderで普通に閲覧ができる(*.appのようなパッケージ形態ではない)
3. 構成パターン
Frameworkの構成はドキュメントに解説がある。
Framework Programming Guide: Anatomy of Framework Bundles
Frameworkを構成するディレクトリのことを "Framework Bundles" と呼ぶ。上記から図を引用していくつかの構成パターンを見てみよう。
シンプルな構成
複数バージョンを含む構成
2つのバージョン(AとB)を含み、Versions/Current のシンボリックリンク先でバージョンの切り替えを行っている。
このタイプは "versioned bundle" とも呼ばれ、1つの Framework Bundlesでアプリケーション毎に異なるバージョンのライブラリを利用することができる。
リソースを含む構成
複数バージョンに加えて、それぞれで英語用のドキュメントリソースを含んでいる。
4. ディレクトリ
Framework Bundles 内に用意できる標準的なディレクトリは次の通り。
Resources | nib, 画像, 音, ローカライズ, 他 |
Headers | 公開ヘッダファイル(*.h) |
Documentation | ドキュメント(HTML, PDF) |
Libraries | Frameworkが必要とする動的ライブラリ |
Info.plist
Framework Bundles 内には通常の Bundleと同様 Info.plist を設置する。
Framework Programming Guide: Anatomy of Framework Bundles より転載。
以下は CoreLocation.framwork の Info.plist の例
/System/Library/Frameworks/CoreLocation.framework/Resources/Info.plist
6. バージョン管理
Framwork Bundles では複数のバージョンを管理できる。
上の例では MyFramework が A,B,C の3つのバージョンを格納しおり、アプリケーションがそれぞれに適したバージョンのライブラリを利用している。
対象は動的ライブラリのみとなる。iOS の場合、動的ライブラリが推奨されない上に、独自で作成したFrameworkを配置して複数アプリケーション(プロセス)間で共有することは行えないので実質的な利用は Mac OS X のみに限られる。
バージョン管理には "Major Versions" と "Minor Versions" の2種類のタイプを使い分ける。
Major Versions
- 非互換バージョン番号 (incompatible version)として位置づけられる
- ある Major Version を対象として作成されたアプリは、別の Major Version のライブラリでは動作しない可能性が高い
- Major Versions は A, B, C, ... アルファベットを使う(のが慣習のようだ)
- Major Version が B の場合、ファイル名は libMyLib.B.dylib のようになる
- 利用にあたっては libMyLib.B.dylib -> libMyLib.dylib のようにシンボリックリンク経由で参照される
- Xcodeで Major Versions を設定するには、ターゲットの情報を開き、Buildタブ→Packaging settings→Framework Version で設定する
Major Versionを変えるケースとして次の項目が挙げられている。
- Removing public interfaces, such as a class, function, method, or structure
- Renaming any public interfaces
- Changing the data layout of a structure
- Adding, changing, or reordering the instance variables of a class
- Adding virtual methods to a C++ class
- Reordering the virtual methods of a C++ class
- Changing C++ compilers or compiler versions
- Changing the signature of a public function or method
Minor Versions
- 互換性のあるバージョン番号(compatible versions)として位置づけられる
- current version と compatibility version の2つの番号が使われる
- current version は Framework のバージョンを表す(修正&リリースの度に増加)
- compatibility version は Framework がサポートする current version を表す
- Frameworkの公開インターフェイス(public interface)が変更された場合、compatibility version を current version と同じにする
- ライブラリのサポート範囲: compatilibility version ≦ アプリが期待するバージョン ≦ current version
- Xcodeで Minor Versions を設定すには、ターゲットの情報を開き Buildタブ→Linking Settings → "Current Library Version" に設定する。必要なら "Compatibility version" も設定する。
Minor Verions を変更するケースとして次の項目が挙げられている。
- Add a class
- Add methods to an Objective-C class
- Add non-virtual methods to a C++ class
- Add public structures
- Add public functions
- Fix bugs that do not change your public interfaces
- Make enhancements that do not change your public interfaces
7. iOS や MacOSX における実際の構成例
CoreLocation.framework を例に実際の構成を見てみる。まずは iPhoneOS4.2.sdk の場合。
[SDKベース]/Platforms/iPhoneOS.platform/Developer/SDKs/ iPhoneOS4.2.sdk/System/Library/Frameworks/CoreLocation.frameworkCoreLocation がライブラリ。他にヘッダファイルを含んでいる。CoreLocation を file コマンドで見てみると arm6 と arm7 の2つのアーキテクチャの動的ライブラリが含まれているのがわかる。
$ file CoreLocation CoreLocation: Mach-O universal binary with 2 architectures CoreLocation (for architecture armv7): Mach-O dynamically linked shared library arm CoreLocation (for architecture armv6): Mach-O dynamically linked shared library armここにはシミュレータ(i386)用のアーキテクチャは含まれない。シミュレータは別のディレクトリに存在する。
[SDKベース]/Platforms/iPhoneSimulator.platform/Developer/SDKs/ iPhoneSimulator4.2.sdk/System/Library/Frameworks/CoreFoundation.frameworkfileコマンドでみると i386用のライブラリが確認できる。
$ file CoreLocation CoreLocation: Mach-O dynamically linked shared library i386シミュレータの方にはローカライズ用のファイルも含まれる。
Japanese.lproj 内には InfoPlist.strings と locationd.strings が含まれる。
InfoPlist.strings /* Localized versions of Info.plist keys */ CFBundleName = "CoreLocation";
locationd.strings /* OK button title */ "OK" = "OK"; /* Yes button title */ "DONT_ALLOW" = "許可しない"; /* " would like to use your current location." */ "LOCATION_CLIENT_PERMISSION" = "“%@”は現在の位置情報を利用します。 よろしいですか?"; : : /* when we detect that the compass is not calibrated and user is not already moving the phone around */ "COMPASS_CALIBRATION" = "コンパスを調整するには、iPhoneを振ってください。";リソースがシミュレータ用Frameworkにあって、実機用Frameworkに含まれないのは、開発上ライブラリは必要だがリソースは実機上には既に存在するはずなので省かれている為だと思われる。
同じ CoreLocation.framework について Mac OS X 10.6 用の Frameworkを見てみる。
[SDKベース]/SDKs/MacOSX10.6.sdk/System/Library/Frameworks/CoreFoundation.framework構成はiOS用と基本的に同じだがシンボリックリンクが使われている。
格納されているアーキテクチャはこんな感じ。
$ file CoreLocation CoreLocation: Mach-O universal binary with 3 architectures CoreLocation (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64 CoreLocation (for architecture i386): Mach-O dynamically linked shared library i386 CoreLocation (for architecture ppc7400): Mach-O dynamically linked shared library ppc64bit、i386の他 ppc も入っている。
上記までは開発用のSDKに含まれる Frameworkを見ていた。今度は Mac OS X 10.6 のシステムで実際に使われている Frameworkを見てみる。
/System/Library/Frameworks/CoreLocation.framework/シミュレータ同様ローカライズ用リソースが入っている。また CoreResources や _CodeSignature といったSDKには無いディレクトリも見られる。
その他、Resources フォルダには画像ファイルなどもファイルを収めることができる。下記は QuickLook.framwork の Resources 配下。
[参考情報] Undocumented Mac OS X:第13回 Universal Binary【後編】 (3/4) - ITmedia エンタープライズ
lipo や file コマンドの使い方など。
8. Umbrella Framework
複数の Frameworkをグループ化してひとつにまとめたものを "Umbrella Framework" と呼ぶ。基本的には通常の Framework と同じ構成を持つが、次の特徴を持つ。
1. 対象となるFrameworkのヘッダファイルを含む
2. 対象となるFrameworkを格納する(Frameworks ディレクトリ)
Mac OS X の場合、Core Services がこれに当たる。CoreServices.framework はこんな感じ。
複数の Framework がFrameworks に格納されているのがわかる。Headers には下記のファイルが格納されている。
CoreServices.h の中身を見ると各 Frameworkのヘッダファイルを読み込んでいるのがわかる。
なお独自の Umbrella Framework 作成は推奨されていない。
Framework Programming Guide: Guidelines for Creating Frameworks - "Dont' Create Umbrella Frameworks"
9. 実行時リンク
※iOS アプリでは動的共有ライブラリは推奨されないので、ここの話は Mac OS X 向けとなる。
Framework 内の動的ライブラリは実行時にそれを必要とするアプリケーションプログラムとリンクされる。Frameworkはメモリ上に1つのみ存在して、それを必要とするすべてのライブラリで共有される。またリンクはアプリケーションが必要とする Framework 内のモジュール(*.o)単位で行われる。
Framework Programming Guide: Frameworks and Binding より
関数 a() が main() 内で呼ばれる時に a.o がリンクされる。同様に関数 b() が doThat() 内で呼ばれるときに b.o がリンクされる。c.o は c() が呼ばれないのでリンクされることは無い。
リンク時にはバージョンチェックが行われ、アプリケーションプログラムが必要とするバージョンをFrameworkがサポートしていない場合、そのアプリケーションプログラムは強制終了する。
その他、Framework は Prebinding(実行時リンクにかかる時間を短縮するためあらかじめリンクに必要な前処理を施しておく)もサポートする。詳しくは下記を参照のこと。
Framework Programming Guide: Frameworks and Binding - Framework and Prebinding
10. Weak Linking
※これは Framework を作る側ではなく、利用する側の話
新しいバージョンのライブラリで提供する機能をアプリケーションプログラムが利用した場合、このアプリケーションプログラムを古いバージョンしか持たない環境で実行した場合クラッシュする。
(例)アプリケーションA がリンクしたライブラリのバージョン 1.2 PC1 に存在するライブラリのバージョン 1.1 PC2 に存在するライブラリのバージョン 1.2アプリケーションAがライブラリバージョン 1.2 で追加された関数を使った場合、PC1ではクラッシュするかあるいは起動できない。Weak Linking の仕組みを使うとこの問題をある程度回避できる。
Weak Linking を使いシンボル(関数やメソッド)をリンクすると、実行時にそのシンボルが存在しなくてもアプリケーションプログラムの実行を継続することができる(ただしそのシンボルを利用=関数呼び出しなど=しようとするとクラッシュする)。この場合は該当するシンボルのアドレスが NULL かどうかチェックし、非NULLの時のみ利用するようにする。Weak Linking を使うには関数のプロトタイプ宣言に weak_import 属性を指定する。
extern int MyFunction() __attribute__((weak_import)); extern int MyVariable __attribute__((weak_import));Weak Linking 指定されたシンボルを使う場合はそのシンボルが NULLかどうかをチェックし、非NULLの場合だけ呼び出すようにする。
extern int MyWeakLinkedFunction() __attribute__((weak_import)); int main() { int result = 0; if (MyWeakLinkedFunction != NULL) { result = MyWeakLinkedFunction(); } return result; }Weak Linkinig設定は Framework全体に対しても行うことができる。この場合は Xcode でフレームワークを選択して「役割」を "Required" から "Weak" に変更することで行える。
あるいはターゲットのビルド設定内の「他のリンクフラグ」に下記のコマンドラインオプションを追加する。
-weak_framework <framework_name>
[参考情報] TN 2064:Ensuring Backwards Binary Compatibility - Weak Linking and Availability Macros on Mac OS X
Weak Linking の日本語解説。a)起動できない、(b)実行中にクラッシュする、(c)正しく実行するが旧版の Mac OS X では事前バインドできない、といったケースについて詳しい解説と、それらの解決策としての Weak Linking についての説明がされている。
[参考情報] iOS 4.0 と iPhone OS 3.x の両方で動作するアプリケーションをビルドする設定 - 24/7 twenty-four seven
Xcode での Weak Linking 設定の仕方など。
11. Frameworkの作成
命名のガイドライン
複数の Framework を使う場合、同名の関数、クラス、メソッド(カテゴリの場合)が存在して問題になるケースがある。これを避けるために命名のガイドラインが設けられている。クラス名の先頭に Framework名に関係した2文字のアルファベットを付与する。
(例)Core Graphics → "CG"
既存クラスへカテゴリを追加する場合、メソッド名称の先頭にも同様に2文字のアルファベットを付ける。
ビルド時設定項目
項目 | 設定方法 |
---|---|
Framework identifier | Info.plist もしくはターゲット:プロパティ:「識別子」 |
Framework version | Major Versionを指す。ターゲット:ビルド:「フレームワークのバージョン」 |
Current version | Minor Versionのひとつ。ターゲット:ビルド:「現在のライブラリのバージョン」 |
Compatibility version | Minor Versionのひとつ。ターゲット:ビルド:「互換バージョン」 |
Exported symbols | ターゲット:ビルド:「書きだされるシンボルのファイル」 |
Installation path | ターゲット:ビルド:「インストールディレクトリ」 |
Preferred address | ※Mac OS X 10.4 以降では不要 |
テスト
開発中の Framework をテストする場合、標準のディレクトリ(/Library/Frameworks)ではなくできれば開発中のディレクトリ(build)内のバイナリをアプリケーションプログラムに使わせたい。この場合、アプリケーションプログラム実行時に環境変数 DYLD_FRAMEWORK_PATH を設定することで任意の場所の Framework を使用させることができる。(例)DYLD_FRAMEWORK_PATH=/Users/hashi/some/frameworks
embed
Framework をアプリケーションプログラムに組み込む(embed)することができる。この場合、Framework をアプリケーショとは別に配布する必要が無い。embed の方法は下記に手順が記載されている。Framework Programming Guide: Creating a Framework
その他
(一般的に動的ライブラリを使用した)Framework は実行時にロードとリンクが行われる為、パフォーマンスが問題になるケースがある(※ただ Prebindingが施されている場合、かかるコストは低減される)。特定のアプリケーションプログラムをターゲットにした Frameworkで他のアプリケーションプログラムと共有しない場合でパフォーマンスが問題になるケースでは embed 形態を検討する。12. Frameworkの初期化
Mac OS X 10.4以降では Framework の初期化と終了処理として "Module Initializers" と "Module Finalizers" が利用できる。
Module Initializers
Module Initializer は引数と戻り値を持たない static関数として記述する。__attribute__((constructor)) static void MyModuleInitializer() { static initialized = 0; if (!initialized) { // Initialization code. initialized = 1; } }呼び出し順序は次の通り。
- Framework内の static変数を初期化
- Module Initilaizers を実行
- Framework内のメソッド、関数の呼び出し(利用)
- Module Initializers は1アプリケーションプログラム(プロセス)毎に Frameworkがロードされた時に1度だけ呼び出される。
- Module Initializers の関数名はエクスポートしない(ビルド設定の「書き出されないシンボルのファイル」で指定する)。
- Module Initializer は複数定義することができる。呼び出し順はコンパイラが遭遇した順番となる。
- Module Initializers はアプリケーションプログラム実行時の引数を参照することもできる。
___attribute__((constructor)) static void initializer(int argc, char** argv, char** envp) { // Initialization code. }
Module Finalizers
Framework の終了時処理を用意することができる。__attribute__((destructor)) static void finalizer() { // Clean up code. }Initializers と同様に複数作成できる。またエクスポートしないようにする。
13. シンボルのエクスポート
デフォルトの動作として Xcodeのリンカは Framework内のすべてのシンボルをエクスポートする。実行時には Dynamic Link editor が Frameworkでエクスポートされているすべてのシンボルを読み込む。この事は外部に公開する必要の無い内部的な関数やメソッドも対象となることを意味する。この為、シンボル数が多くなりロード時間の増加、所要メモリの増加といった問題が生じるケースもある。これを避けるためにXcodeでは公開するシンボルを明示的に指定してリンクさせることができるオプションが用意されている。手順は次のとおり。
1. エクスポートするシンボルを書きだしたテキストファイル(*.txt)を用意する。
記述するシンボル名は nm コマンドで一覧したものを使う。
(例)$ nm CoreLocation 0000000000007a06 s stub helpers 000000000000666a t +[CLLocationManager locationServicesCapable] 0000000000005d10 t +[CLLocationManager locationServicesEnabled] : U _ACInterfaceDeviceNameCopy U _AuthorizationMakeExternalForm U _CFBundleCopyExecutableURL : 0000000000002bf6 T _CLClientAddReferenceLocation 0000000000003bec T _CLClientAllocate 0000000000003053 T _CLClientClearNvramData 0000000000004078 T _CLClientCreate 00000000000037e3 T _CLClientCreateSu :これらを元に次のようなファイルを作成する。
_CLClientAddReferenceLocation _CLClientAllocate #_CLClientClearNvramData先頭に # を付けた場合は無視される。
2. 作成したテキストファイルをプロジェクトの Resources へ追加する。
3. ターゲットの情報を開きビルドタブ内の「書きだされるシンボルのファイル」にこのファイル名を指定する。
逆に一部のシンボルのみ公開したくない場合はその一覧が記載されたテキストファイルを作成し「書き出されないシンボルのファイル」に指定する。
[参考情報] nm(1) Mac OS X Developer Tools Manual Page
14. Frameworkの配置
Framework の配置場所は /Library/Frameworks とそれ以外の2つに分けられる。
/Library/Frameworks
システムで標準に用意されているディレクトリ。複数のアプリケーション間で共有して利用する場合はこのディレクトリへ配置する。メリットは、コンパイル時および実行時に標準で検索対象となること。それ以外の場所
~/Library/Frameworks や /Network/Library/Frameworks、その他自由に配置することができる。特定のアプリケーションでのみ使用する Frameworkなどはこれらのディレクトリへ配置する。自由に配置できる反面、(1)標準の検索対象にならない(2)起動時間が遅くなるといったデメリットがある。(1)については、アプリケーションプログラム側でビルドする時に設定する必要がある。(2)については、ネットワーク上の他のサーバに上記ディレクトリが存在する場合はそれを利用するアプリケーションの起動に時間がかかる場合があることを意味している。/System/Library/Frameworks
Frameworkの配置場所として実はもう一つこのディレクトリが存在する。ただしこのディレクトリは Appleが提供する Frameworkのみ設置が可能で独自(サードパーティ製)の Frameworkはここには設置してはいけない。検索順序
実行時の Framework検索順序は次の通り。- ビルド時(リンク時)に指定されたパス ※これは実行バイナリに埋めこまれている
- /Library/Frameworks
- /System/Library/Frameworks
15. 運用パターン
Framework の開発から配布・利用までの運用パターンを考えてみる。
Mac OS X - 典型パターン
典型的な利用方法としては動的ライブラリを含む Framework を用意して開発時にヘッダファイルを使用し、実行時にリンクして使用する。アプリケーションとは別に実行環境へ Framework をインストールする必要がある。メリットとしては1つの Framework を複数のアプリケーション(プロセス)で共有することができる。
Mac OS X - embedパターン
Framework はアプリケーションバンドルに埋め込む (embed)することもできる。この場合、実行環境に Frameworkがインストールされている必要は無い。メリットは、配布が簡単で確実に実行できること。
iOS - 典型パターン
一方 iOS の場合は開発時のみ使われる。静的ライブラリはアプリケーションバイナリの一部として構成され一緒に配布される。iOS の場合、Mac OS X のように実機の /Library/Frameworks などへ動的ライブラリを配置することはできない。この為、開発時のみに使う静的ライブラリとヘッダファイルのプレースホルダ的な使い方となる。
関連情報
Software Delivery Guide: Introduction
ソフトウェア配布のガイド。Frameworkの配布方法についても触れられている。
Launch Time Performance Guidelines: Introduction to Launch Time Performance Guidelines
ソフトウェア起動時間に関するガイド。Frameworkにおける Tipsも記載されている。
登録:
投稿 (Atom)
人気の投稿(過去 30日間)
-
[2011-07-08追記] ブロックの原因が判明、下記もどうぞ。 Cocoaの日々: [iOS] SCNetworkReachabilityGetFlags のブロックの件 以前、 Cocoaの日々: ネットワーク接続状況を知る というブログを書いた。 その後わか...
-
UIPageControl を追加してみた。 追加自体は簡単で UIPageControl のインスタンスを作成し、表示したい UIViewに追加するだけ。 // setup page control CGRect pageControlFrame = CGRectMak...
-
前回検証したトランジションを利用してスライドショー再生の実装に入る。 Cocoaの日々: トランジション[3] CATransition を使う(その2) 仕組み トランジションは kCATransitionFade(ディゾルブ)を使う。CATransitionは2つ...
-
[前回] Cocoaの日々: 簡易スライドビューア [15] スライドショー雛形(2) 調整 ピンチやフリックでスライドショーが停止しない。前回の実装ではイベントハンドリングが不足しているのでこれを直す。 #もはやタイトルが何のことだがわからないが... イベント処...
-
tigはコマンドラインベースの git ツール。見た目が結構カラフル。 公式サイト Index of /tig インストール 環境:Mac OS X 10.6.4 下記より最新版のソースコードをダウンロードできる。 Index of /tig/releases...