Xcode 6.1 Beta 3で仕様が変更され,Any
タイプの変数に関数タイプを代入できるようになりましたので,以下の話は無意味です。
関数のMirrorTypeから関数に戻す方法を試行錯誤してみました.
結果,いとも簡単に鏡の中(リフレクション)からクロージャを引っこ抜くことができました.
Swiftのリファレンスには
Any
can represent an instance of any type at all, apart from function types.
とあり,確かに
let f: Any = {() -> () in println("Hello!")}
//Segmentation fault: 11
Any
で関数タイプを表現できません(クラッシュするのはどうかとも思いますが).
しかし,リフレクション(reflect関数
)を使って鏡の中(MirrorType
)を覗くと,
let mr = reflect({() -> () in println("Hello!")})
println(mr.value)
// (Function)
しっかりと関数が入っています.MirrorType
のvalue
はAny
タイプであるにも関わらず!
そして関数を取り出そうとすると,
let f = mr.value as () -> ()
// Segmentation fault: 11
やっぱり落ちます...うーむ...
unsafeBitCastを使って強引に引っ張り出してみる.
コマンドラインのswiftインタプリタでmr.value
を表示させると,
24> let s = mr.value
s: Any = {
payload_data_0 = {}
payload_data_1 = {}
payload_data_2 =
instance_type = {}
}
25> sizeofValue(s)
$R9: Int = 32
ふむふむ.表示内容から見てpayload_data_
の0,1にクロージャが入っていそうです.そしてサイズは全部で32バイトと出たのでpayload_data_
とinstance_type
はそれぞれ8バイトずつと思われます.
一方,クロージャのサイズはsizeofすると16バイトと出ました.
unsafeBitCast
関数でAny
から32(=16+8+8)バイトのタプルにコピーしてみましょう.
typealias AF = (() -> (), Int, Int)
let t = unsafeBitCast(s, AF.self)
let f = t.0
f()
// Hello!
無事,元の関数を取り出せました.強引ですけどね.
クロージャのキャスト関数を定義
このキャストは便利そうなので関数にしておきます.
(クロージャはどんなタイプでもサイズは16バイトなようなです.)
func anyToFuncCast<F>(any: Any, ftype: F.Type) -> F {
assert(sizeofValue(any) == 32)
typealias AF = (F, Int, Int)
let t = unsafeBitCast(any, AF.self)
return t.0
}
まとめ
仕様的にありえないはず(?)の
Any
タイプから関数タイプへの変換は,unsafeBitCast
を使えば可能.MirrorType
はドキュメントもないので仕様が適当なのでしょう.そのうち変わるかも.このパターンのようにメモリ内容をだいたい把握できるときは,タプルと
unsafeBitCast
を使えば強引にキャストできるかも.