Xcode4の使い方メモ 〜 ライブラリを作る

※実行ファイルの生成パスを、Xcode>Preferences>LocationsでRelativeに指定しているという前提で話を進めます。

Static Library(.a)を作る

まず静的ライブラリ作成用プロジェクトを用意します。(StaticLib.xcodeproj)
File>New>New Project>MacOSX>Framework & Library>Next>Product Name>名称記入, Type>Static, >プロジェクトのパスを指定する>Create
(StaticLib.xcodeprojが存在するパスをと表記することにします。)
サンプルとして以下のファイルを追加します。
StaticLibIF.h

class StaticLibIF{
public:
    static void execute();
};

StaticLibIF.cpp

#include <iostream>
#include "StaticLibIF.h"

void StaticLibIF::execute(){
    std::cout << "StaticLibIF::execute was called" << std::endl;
}

ビルドすると、//DerivedData/StaticLib/Build/Products/Debug に静的ライブラリ(.a)が作成されています。

Static Link Libraryを利用する

作成したStatic Libraryを使うためにCommandLineTool用のプロジェクト(StaticLibUse.xcodeproj)を作成しておきます。

Static Libraryプロジェクトの追加

File>Add Files To "プロジェクト名">StaticLib.xcodeprojを選択>Add
TARGET>Build Paths>Link Binary With Libraries>Workspace名>StaticLib.a>Add
これで実行ファイル作成プロジェクト内に入れ子状にライブラリ作成プロジェクトが追加されました。

インクルードパスの設定

Static Libraryを使うためのヘッダ(ここでは"StaticLibIF.h")がありますので、インクルードパスを指定しておきます。
TARGET>Build Settings>Serach Paths>Header Search Paths>ターゲット名の部分を選択>"+"
インクルードパスを設定する>Done, 再起的にインクルードする場合はRecursiveにチェックを入れる

スキームの設定

本体プロジェクトとともにライブラリもmakeされるようにスキームを編集して依存関係を設定します。
Product>Edit Scheme>Build>"+">StaticLibの方を選択>Add
これでビルドすれば両方のプロジェクトがビルドされて、ライブラリと実行ファイルが作成されます。実行してみてStaticLIbIF::execute内で示した文字列がコンソールに表示されていればOKです。

Dynamic Library(.dylib)を作る

先ほどと同様に動的ライブラリ作成用プロジェクトを用意します。(DynamicLib.xcodeproj)
File>New>New Project>MacOSX>Framework & Library>Next>Product Name>名称記入, Type>Dynamic, >プロジェクトのパスを指定して"Create"
(改めてDynamicLib.xcodeprojが存在するパスをと表記することにします。)
サンプルとして以下のファイルを追加します。
DylibIF.h

class DylibIF{
public:
    DylibIF();
    ~DylibIF();
    virtual void execute();
};

extern "C" DylibIF* GetInterface(void);
typedef DylibIF* IFCreator(void);

extern "C" void DestroyInterface(DylibIF*);
typedef void IFDestructor(DylibIF*);

DylibIF.cpp

#include <iostream>
#include "DynamicLibIF.h"

DylibIF::DylibIF(){
    std::cout << "DylibIF::DylibIF was called" << std::endl;
}

DylibIF::~DylibIF(){
    std::cout << "DylibIF::~DylibIF was called" << std::endl;
}

void DylibIF::execute(){
    std::cout << "DylibIF::execute was called" << std::endl;
}

DylibIF* GetInterface(void){
    return new DylibIF();
}

void DestroyInterface(DylibIF* instance){
    delete instance;
    instance = NULL;
}

ビルドすると、//DerivedData/DynamicLib/Build/Products/Debug に動的ライブラリ(.dylib)が作成されています。

Dynamic Libraryを利用する

作成したライブラリを使うCommandLineToolプロジェクト(DynamicLibUse.xcodeproj)を作成しておきます。

dylibのインストールと実行時パス設定

dylibは実行時に利用しますので、利用する側のプロジェクトをビルドする際にライブラリもリンクする必要はありません。LinuxのShared Library(.so)と同様に、実行時にライブラリを検索してはじめに見つかったものを実行します。検索されるパスの優先順位は、
環境変数LD_LIBRARY_PATHで設定されているパス, 作業ディレクトリ(実行ファイルと同じパス), システムデフォルトのパスになります。
とりあえず動作確認してみるには実行バイナリファイルが作成された場所にコピーしてみてください。→
//DerivedData/DynamicLibraryUse/Build/Products/Debug/libDynamicLib.dylib
OSXでは/usr/libにdylibがありますのでそこにコピーしても動きます。または自分で環境変数で指定するならば、./lib/libDynamicLib.dylibなどにコピーしておいて、
Produces>Edit Scheme>Run>Environment Variables>"+">NameにLD_LIBRARY_PATH、Valuesに"./lib"を設定してもよいです。(もちろん絶対パス指定でもOK.)

dylibの呼び出し

まずは呼び出しサンプルです。(エラー処理省略)

#include <iostream>
#include <dlfcn.h>
#include "DynamicLibIF.h"
int main (int argc, const char * argv[]) {

    // dynamic libraryを開く
    void* dylib_handle = dlopen("libDynamicLib.dylib", RTLD_LOCAL);
    // NULLチェック

    // dlsym:シンボルをvoid*型として取り出す→ここでは関数ポインタを取り出している
    IFCreator* GetInterface = (IFCreator*)dlsym(dylib_handle, "GetInterface");
    IFDestructor* DestroyInterface = (IFDestructor*)dlsym(dylib_handle, "DestroyInterface");
    // NULLチェック

    // 取り出した関数実行
    DylibIF* dylibIF = GetInterface();  // dylib内クラスのインスタンス生成
    dylibIF->execute();                 // 本処理実行
    DestroyInterface(dylibIF);          // dylib内クラスのインスタンス破棄

    dlclose(dylib_handle);    // dynamic libraryを閉じる
   // NULLチェック

    return 0;
}

ライブラリを呼び出すのに必要なヘッダをインクルードしておきます。

#include <dlfcn.h>

モジュールを開く・閉じるにはそれぞれdlopen, dlcloseを利用して、externで指定したシンボルを取得するのにdlsymを使います。dlsymの戻り値はvoid*型なのでキャストしないといけません。キャストする型を予め定義しておくとよいと思います。クラスでなく、グローバル関数や変数を取得する場合も同様です。

typedef DylibIF* IFCreator(void);

dylib内部のクラスのインスタンスを取り出して、そのメソッドを呼んでいますが、DylibIF.hにて仮想関数で定義しないとうまくビルドが通らないので注意してください。
最後に、実行してみて以下のメッセージが出れば成功です。

DylibIF::DylibIF was called
DylibIF::execute was called
DylibIF::~DylibIF was called

ちなみにdylibが見つからなかったときはエラーメッセージが出力されます。

dlopen(指定したライブラリ名.dylib, 4):image not found