はじめに
型が不明なインスタンスを Object Pascal から使う方法をメモしておきます。
概要
今回遭遇したのは渡した引数によって返ってくるインスタンスが NSDictionary か NSArray のどちらかという凶悪なライブラリ。
型に厳格な我らが Object Pascal ではとてもピンチな状況でした。
…ていうか Objective-C だって、これ面倒でしょ…
Objective-C の型
Objective-C は、セレクタ(メソッド)が呼べるかどうか実行時に動的に解決します。
そのため、例えば NSDictionary と NSArray のどちらかのインスタンス obj があったとき
int c = [obj count];
こんなコードが動作します。
これは NSDictionary にも NSArray にも同じ名前のセレクタがあるからです。
Object Pascal の型
Object Pascal は Pascal なので型に非常に厳格です。
なので、基本的には別のクラスに同じ名前のメソッドがあっても呼び出すことはできません(基本的じゃない例外は COM のオートメーションオブジェクト)。
var
C: Integer;
Obj: NSObject;
begin
Obj := ArrayObject; // NSArray のインスタンスを Obj に代入
// NSObject には Count は無いので呼べない
C := Obj.count;
// このようにキャストが必要
C := TNSArray.Wrap(Obj).count;
end.
型が不明なインスタンスの型を決定する
ということで、Object Pascal で Objective-C のインスタンスを呼ぶ時、型が不明ながらも今回のようにある程度決まっている場合は
[NSObject isKindOfClass]
が使えます。
先ほどの例では
var
C: Integer;
Obj: NSObject;
begin
Obj := Somethig; // なにか
// NSDictionary なら NSDictionary にキャスト
if obj.isKindOfClass(objc_getClass('NSDictionary')) then
C := TNSDictionary.Wrap(Obj).count;
// NSArray なら NSArray にキャスト
if obj.isKindOfClass(objc_getClass('NSArray')) then
C := TNSArray.Wrap(Obj).count;
end.
こんな風に書けます。
これで普通にアクセス出来るよ!やったね!!
objc_getClass について
isKindOfClass の引数に Objective-C のクラスを渡す必要があるため、
objc_getClass('クラス名') // Macapi.ObjCRuntime ユニットで定義
を使いました。
objc_getClass を使うとクラス名文字列から Objective-C クラスへのポインタが返ってきます。
クラスを要求するメソッドでは、この関数が使えるので覚えておくと希に良いことがあるかもしれません!
まとめ
クラスがある程度判っている場合は isKindeOfClass が使えます!
呼び出しには objc_getClass を使います!
おまけ:セレクタがあるか?を特定する方法
[NSObject respondsToSelector]
を使います。
が、Object Pascal では型が確定しないと、そのセレクタ(メソッド)も呼び出せないので、あまり意味が無いと思います。
(実際 FireMonkey のソース内でも使われていません)
それでは!アデュー!