[Mac][iOS] Frameworkとは?

2010年11月18日木曜日 | Published in | 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なら可能かもしれない。


目次


今回は長いので目次を用意した。

  1. Frameworkとは?
  2. 特徴
  3. 構成パターン
  4. ディレクトリ
  5. Info.plist
  6. バージョン管理
  7. iOS や MacOSX における実際の構成例
  8. Umbrella Framework
  9. 実行時リンク
  10. Weak Linking
  11. Frameworkの作成
  12. Frameworkの初期化
  13. シンボルのエクスポート
  14. Frameworkの配置
  15. 運用モデル
  16. 関連情報


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 内に用意できる標準的なディレクトリは次の通り。
Resourcesnib, 画像, 音, ローカライズ, 他
Headers公開ヘッダファイル(*.h)
Documentationドキュメント(HTML, PDF)
LibrariesFrameworkが必要とする動的ライブラリ



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 versioncompatibility 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.framework
CoreLocation がライブラリ。他にヘッダファイルを含んでいる。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.framework
fileコマンドでみると 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 ppc
64bit、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 identifierInfo.plist もしくはターゲット:プロパティ:「識別子」
Framework versionMajor Versionを指す。ターゲット:ビルド:「フレームワークのバージョン」
Current versionMinor Versionのひとつ。ターゲット:ビルド:「現在のライブラリのバージョン」
Compatibility versionMinor 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;
    }
}
呼び出し順序は次の通り。
  1. Framework内の static変数を初期化
  2. Module Initilaizers を実行
  3. 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検索順序は次の通り。

  1. ビルド時(リンク時)に指定されたパス ※これは実行バイナリに埋めこまれている
  2. /Library/Frameworks
  3. /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も記載されている。

Responses

Leave a Response

人気の投稿(過去 30日間)