Xamarin.iOS でのネイティブ ライブラリの参照

Xamarin.iOS は、ネイティブ C ライブラリと Objective-C ライブラリの両方とのリンクをサポートしています。 このドキュメントでは、ネイティブ C ライブラリを Xamarin.iOS プロジェクトにリンクする方法について説明します。 Objective-C ライブラリに対しても同じ操作を行う場合は、Objective-C の種類のバインドに関するドキュメントを参照してください。

ユニバーサル ネイティブ ライブラリをビルドする (i386、ARMv7、ARM64)

多くの場合、iOS 開発でサポートされている各プラットフォーム (Simulator 向けは i386、デバイス自体向けは ARMv7/ARM64) 用にネイティブ ライブラリを構築することが望ましい場合があります。 ライブラリ用の Xcode プロジェクトが既にある場合、これは非常に簡単に行えます。

i386 バージョンのネイティブ ライブラリをビルドするには、ターミナルから次のコマンドを実行します。

/Developer/usr/bin/xcodebuild -project MyProject.xcodeproj -target MyLibrary -sdk iphonesimulator -arch i386 -configuration Release clean build

これにより、MyProject.xcodeproj/build/Release-iphonesimulator/ の下にネイティブ スタティック ライブラリが作成されます。 ライブラリ アーカイブ ファイル (libMyLibrary.a) を後で使用できるように安全な場所にコピー (または移動) し、次にビルドする同じライブラリの arm64 および armv7 バージョンと競合しないように、一意の名前 (libMyLibrary-i386.a など) を付けます。

ネイティブ ライブラリの ARM64 バージョンをビルドするには、次のコマンドを実行します。

/Developer/usr/bin/xcodebuild -project MyProject.xcodeproj -target MyLibrary -sdk iphoneos -arch arm64 -configuration Release clean build

今回は、ビルドされたネイティブ ライブラリが MyProject.xcodeproj/build/Release-iphoneos/ に配置されます。 もう一度、このファイルを安全な場所にコピー (または移動) し、競合が起きないように libMyLibrary-arm64.a のような名前に変更します。

次に、ライブラリの ARMv7 バージョンをビルドします。

/Developer/usr/bin/xcodebuild -project MyProject.xcodeproj -target MyLibrary -sdk iphoneos -arch armv7 -configuration Release clean build

結果のライブラリ ファイルを他の 2 つのバージョンのライブラリを移動したのと同じ場所にコピー (または移動) し、libMyLibrary-armv7.a のような名前に変更します。

ユニバーサル バイナリを作成するには、次のような lipo ツールを使用できます。

lipo -create -output libMyLibrary.a libMyLibrary-i386.a libMyLibrary-arm64.a libMyLibrary-armv7.a

これにより、すべての iOS 開発ターゲットに使用するのに適したユニバーサル (ファット) ライブラリになる libMyLibrary.a が作成されます。

欠落した必要なアーキテクチャ i386

iOS Simulator で Objective-C ライブラリを使用しようとしたときにランタイム出力に does not implement methodSignatureForSelector または does not implement doesNotRecognizeSelector メッセージが表示される場合は、ライブラリが i386 アーキテクチャ用にコンパイルされていない可能性があります (上記の「ユニバーサル ネイティブ ライブラリをビルドする」セクションを参照)。

特定のライブラリでサポートされているアーキテクチャを確認するには、ターミナルで次のコマンドを使用します。

lipo -info /full/path/to/libraryname.a

ここで、/full/path/to/ は使用されているライブラリへの完全なパスで、libraryname.a は対象のライブラリの名前です。

ライブラリのソースがある場合は、それを i386 アーキテクチャ用にもコンパイルしてバンドルする必要があります (iOS Simulator でアプリをテストしたい場合)。

ライブラリのリンク付け

使用するサードパーティ製ライブラリは、アプリケーションに静的にリンクされている必要があります。

インターネットから取得したライブラリ "libMyLibrary.a" を静的にリンクするか、Xcode を使用してビルドする場合は、いくつかの操作を行う必要があります。

  • ライブラリをプロジェクトに取り込む
  • ライブラリをリンクするように Xamarin.iOS を構成する
  • ライブラリからメソッドにアクセスする

ライブラリをプロジェクトに取り込むには、ソリューション エクスプローラーからプロジェクトを選択し、Command+Option+a キーを押します。 libMyLibrary.a に移動し、それをプロジェクトに追加します。 メッセージが表示されたら、Visual Studio for Mac または Visual Studio にプロジェクトにそれをコピーするように指示します。 追加した後、プロジェクトで libFoo.a を見つけて右クリックし、[ビルド アクション][なし] に設定します。

Xamarin.iOS を構成してライブラリをリンクするには、最終的な実行可能ファイル (ライブラリ自体ではなく、最終的なプログラム) のプロジェクト オプションで、iOS ビルドの Extra 引数 (これらはプロジェクト オプションの一部です) に "-gcc_flags" オプションを追加し、プログラムに必要なすべての追加ライブラリを含む引用符で囲まれた文字列を追加する必要があります。例:

-gcc_flags "-L$(MSBuildProjectDirectory) -lMylibrary -force_load $(MSBuildProjectDirectory)/libMyLibrary.a"

上記の例では、libMyLibrary.a をリンクします。

-gcc_flags を使用して、実行可能ファイルの最終リンクを実行するために使用する GCC コンパイラに渡すコマンド ライン引数のセットを指定できます。 たとえば、次のコマンド ラインでは、CFNetwork フレームワークも参照されます。

-gcc_flags "-L$(MSBuildProjectDirectory) -lMylibrary -lSystemLibrary -framework CFNetwork -force_load $(MSBuildProjectDirectory)/libMyLibrary.a"

ネイティブ ライブラリに C++ コードが含まれている場合は、Xamarin.iOS が正しいコンパイラを使用することを認識できるように、"Extra 引数" に -cxx フラグを渡す必要もあります。 C++ の場合、上記のオプションは次のようになります。

-cxx -gcc_flags "-L$(MSBuildProjectDirectory) -lMylibrary -lSystemLibrary -framework CFNetwork -force_load $(MSBuildProjectDirectory)/libMyLibrary.a"

C# から C メソッドにアクセスする

iOS では、次の 2 種類のネイティブ ライブラリを使用できます。

  • オペレーティング システムの一部である共有ライブラリ。

  • アプリケーションに付属するスタティック ライブラリ。

これらのいずれかで定義されているメソッドにアクセスするには、Mono の P/Invoke 機能を使用します。これは、.NET で使用するのと同じテクノロジです。これは大まかに以下を行います。

  • 呼び出す C 関数を決定する
  • シグネチャを決定する
  • どのライブラリに配置するかを決定する
  • 適切な P/Invoke 宣言を記述する

P/Invoke を使用する場合は、リンクするライブラリのパスを指定する必要があります。 iOS 共有ライブラリを使用する場合は、パスをハードコーディングすることも、Constants で定義した便利な定数を使用することもできます。これらの定数は、iOS 共有ライブラリをカバーしている必要があります。

たとえば、C でこのシグネチャを持つ Apple の UIKit ライブラリから UIRectFrameUsingBlendMode メソッドを呼び出す場合は、次のようにします。

void UIRectFrameUsingBlendMode (CGRect rect, CGBlendMode mode);

P/Invoke 宣言は次のようになります。

[DllImport (Constants.UIKitLibrary,EntryPoint="UIRectFrameUsingBlendMode")]
public extern static void RectFrameUsingBlendMode (RectangleF rect, CGBlendMode blendMode);

Constants.UIKitLibrary は、単に "/System/Library/Frameworks/UIKit.framework/UIKit" として定義された定数に過ぎません。EntryPoint では、必要に応じて外部名 (UIRectFramUsingBlendMode) を指定して、C# で別の名前 (短い名前 RectFrameUsingBlendMode) を公開できます。

C Dylibs へのアクセス

Xamarin.iOS アプリケーションで C Dylib を使用する必要がある場合は、DllImport 属性を呼び出す前にいくつかの追加のセットアップが必要です。

たとえば、アプリケーションで呼び出す Animal_Version メソッドを持つ Animal.dylib がある場合は、それを使用する前に、ライブラリの場所を Xamarin.iOS に知らせる必要があります。

これを行うには、Main.CS ファイルを次のように編集します。

static void Main (string[] args)
{
    // Load Dylib
    MonoTouch.ObjCRuntime.Dlfcn.dlopen ("/full/path/to/Animal.dylib", 0);

    // Start application
    UIApplication.Main (args, null, "AppDelegate");
}

ここで、/full/path/to/ は、使用されている Dylib への完全なパスです。 このコードを配置すると、次のように Animal_Version メソッドにリンクできます。

[DllImport("Animal.dylib", EntryPoint="Animal_Version")]
public static extern double AnimalLibraryVersion();

スタティック ライブラリ

iOS ではスタティック ライブラリのみを使用できるため、リンクする外部共有ライブラリがないため、DllImport 属性のパス パラメーターは、パス名ではなく、特殊な名前 __Internal (名前の先頭の 2 つのアンダースコア文字に注意) を使用する必要があります。

これにより、DllImport は、共有ライブラリから読み込むのではなく、現在のプログラムで参照しているメソッドのシンボルを検索するように強制されます。