[2017-02-12 update] swift3からunsafeBitCastではなくUnmanagedを使う方法が推奨となっていますのでそちらをお使いください。
unsafeBitCastを使ってswiftのクラスをポインタとして扱い、コールバック後のクラスに戻して動作するかやってみた。
結論から言うと動いた。これでAppleのフレームワークのせいでObj-Cを書かなくてはいけないケースは無くなった。
swiftからコールバック関数を利用するC言語のSDKを使う場合、これは基本的なテクニックになりそうだ。
XXX-Bridging-Header.h
#include "cfile.h"
cfile.h
typedef void (*CALLBACK)(void*, int); void registerCallback(CALLBACK callback, void* ref); void invoke(void);
cfile.c
#include "cfile.h" static CALLBACK s_callback; static void* s_ref; void registerCallback(CALLBACK callback, void* ref) { s_callback = callback; s_ref = ref; } void invoke(void) { for (int i = 0; i < 5; ++i) { (*s_callback)(s_ref, i); } }
main.swift
class Engine { let callback: @convention(c) (UnsafeMutableRawPointer?, Int32) -> Void = { (ref, count) in if let ref_unwrapped = ref { let engine:Engine = unsafeBitCast(ref_unwrapped, to:Engine.self) let ret = engine.square(val:count) print("\(ret)") } } init(){ let ref: UnsafeMutableRawPointer = unsafeBitCast(self, to: UnsafeMutableRawPointer.self) registerCallback(callback, ref); } func execute() { invoke() } func square(val:Int32) -> Int32 { return val*val } } // 0 // 1 // 4 // 9 // 16
[2017-01-21 改訂]
swift3になってvoid*型はオプショナルに変化しました。
nilかどうかチェックできるので少し安全性が高まりました。