関数ポインタ型をとるC言語インタフェースのライブラリに、Objective-C言語のデリゲート(Delegate)を指定する方法。ARC(Automatic Reference Counting)環境を想定。
// XxxLibライブラリ C言語/公開インタフェース // コールバック関数ポインタ型 // nValue: ライブラリから通知される値 // pUserData: 登録時に設定する任意ポインタ値 typedef int (*CallbackFunc)(int nValue, void *pUserData); // コールバック関数(関数ポインタ)を登録する int XxxLib_RegisterCallback(CallbackFunc pFn, void *pUserData); // 登録されたコールバック関数を呼び出す int XxxLib_InvokeCallback();
Objective-Cレイヤでライブラリラッパー(XxxLibWrap
)インタフェースを用意する。また、Cライブラリに登録したい関数ポインタに対応したデリゲート(XxxLibDelegate
)プロトコルも宣言する。ライブラリラッパー層には、デリゲートを登録(registerCallback
)する。
// XxxLibWrap.h: Objective-C言語/ラッパー層ヘッダ #import <Foundation/Foundation.h> @protocol XxxLibDelegate @required - (int)onCallback:(int)nValue; @end @interface XxxLib : NSObject - (int)registerCallback:(id<XxxLibDelegate>)delegate; - (int)invokeCallbak; @end
コールバック関数にはC言語サンク関数(CallbackThunk
)を登録しておき、そこからObjective-C言語デリゲートのメソッドへと転送する。このとき C/void*
型 ⇔ Objective-C/id<XxxLibDelegate>
型 の相互変換を行うために、型キャスト時に __bridge 修飾子が必要となる。
// XxxLibWrap.m: Objective-C言語/ラッパー層実装 #import <Foundation/Foundation.h> #import "XxxLibWrap.h" #import "XxxLib.h" static int CallbackThunk(int nValue, void *pUserData) { // void*型ユーザデータからデリゲートid型を復元(ARCカウント操作なし) id<XxxLibDelegate> self_ = (__bridge id<XxxLibDelegate>)pUserData; return [self_ onCallback:nValue]; } @implementation XxxLib - (int)registerCallback:(id<XxxLibDelegate>)delegate { // デリゲートid型から汎用ポインタ(void*)型にキャスト(ARCカウント操作なし) return XxxLib_RegisterCallback(&CallbackThunk, (__bridge void*)delegate); } - (int)invokeCallbak { return XxxLib_InvokeCallback(); } @end
注意:__bridge修飾キャストではid<XxxLibDelegate>
型のARC参照カウントを操作しないため、Objective-C側でXxxLibDelegate
プロトコルを採用したインタフェースのインスタンス存続期間管理に注意すること。つまり、コールバック関数が呼ばれうる期間は、プログラマの責任でObjective-C側インスタンスの生存を保証する。