【コラム】

ダイナミックObjective-C

12 ポージングで乗っ取り

    木下誠  [2005/10/25]

    クラスの乗っ取り

    Objective-Cには、ポージングという機能がある。これは、一言でいうと、既存のクラスを「乗っ取る」ことができる機能だ。すでにあるクラスを、強引に自分のクラスで置き換えてしまう。

    ポージングは、poseAsClass:というメソッドで行う。このメソッドが呼ばれたクラスは、引数で渡されたクラスのように振る舞うことになる。これは、具体的な例を見てもらうのが早いだろう。

    例として、Cocoaでウィンドウを表すクラスであるNSWindowを継承した、TransparentWindowというクラスを作ってみた。クラスがランタイムに読み込まれたときに呼ばれる、loadメソッドの中で、poseAsClass:を呼んでいる。

    // TransparentWindowクラスの宣言
    @interface TransparentWindow : NSWindow
    {}
    @end

    // TransparentWindowクラスの実装
    @implementation TransparentWindow

    // クラスがランタイムに読み込まれたときに、呼ばれる
    + (void)load
    {
        // TransparentWindowを、NSWindowのように振る舞わせる
        [self poseAsClass:[NSWindow class]];
    }

    // ウィンドウを表示するときのアルファ値を返す
    - (float)alphaValue
    {
        return 0.6f;
    }

    @end

    これで、TransparentWindowはNSWindowのように振る舞う。

    さて、このクラスでは、alphaValueというメソッドを実装している。これはウィンドウを表示するときのアルファ値を指定するメソッドであり、1.0以下の値を返すと、半透明表示になる。つまり、アプリケーションの実行中、このウィンドウは半透明で表示される。

    ところが、このウィンドウだけではない。ポージングにより、このクラスはNSWindowとして動作する。つまり、このアプリケーションのすべてのウィンドウ、システム側で用意されるものも含めて、半透明になるのだ。

    次の図を見てほしい。真ん中に表示されているのがTransparentWindowだが、同時に表示しているフォントパネルやカラーパネルも半透明化されている。これらのパネルはNSWindowを継承しており、TransparentWindowのポージングの影響を受けているのだ。

    すべてのウィンドウが半透明化されている

    ポージングの制約

    このように強力なポージングだが、何にでも使える訳ではない。ポージングができるクラスには、制約がある。

    あるクラスClassを、別のクラスPseudoClassでポージングするには、以下の条件が必要になる。

    • PseudoClassは、Classの直接のサブクラスである。
    • PseudoClassは、新たなインスタン変数を追加していない。メソッドの追加や上書きは、かまわない。
    • Classのインスタンスが作られる前に、PseudoClassのposeAsClass:を呼び出す。

    これだけの条件が揃って、ポージングが可能になる。特に、インスタンス変数の追加が行えない点は、実用上、残念ではある。

    クラス階層の変化

    ポージングを行うと、クラス階層が変更になる。先ほどのTransparentWindowで説明しよう。

    ポージングを行う前は、次のようなクラス階層になっている。NSPanelは、フォントパネルのようなフローティングウィンドウを含む、パネルを表すクラスだ。

    これが、poseAsClass:を呼び出すと、次のように変わる。

    %NSWindowというクラスが新しくできている。そして、NSWindowとTransparentWindowは、このクラスを継承している。

    %NSWindowは、元のNSWindowと同じクラスになる。そして、この図でのNSWindowは、実はTransparentWindowをコピーしたものになっている。これにより、NSWindowへのメッセージは、TransparentWindowに送られるのと同じことになる。

    つまり、ポージングは、動的なクラス階層の変化と、動的なメソッドの入れ替えによって実現されているのだ。Objective-Cの動的特性も、ここに極まれり、といった感じである。

    ポージングを提供する、Objective-Cの方針

    ポージングは、ドキュメントによれば、デバッグに役に立つ、とある。だがこれは、どう考えてもハックのための機能だろう。実際、Mac OS Xでの、Cocoaベースのシステムユーティリティ系のソフトは、この機能を使い倒している。ポージングがあったからこそ、実現できている機能も多い。

    だが、推測はつくだろうが、ここまで強力な機能だと、下手に使うと不安定になる。ちょっと使い方を誤ると、クラッシュしてしまう。それでもObjective-Cは、ポージングを提供する。設計の方針として、可能な限りの機能を提供しようとしていることがうかがえる。そのかわり、プログラムの安定性に責任があるのは、プログラマである。プログラミング言語ではない。

    新着記事

    特設サイトの情報

      求人情報

      人気記事

      一覧

      イチオシ記事

      新着記事

      特別企画

      転職ノウハウ

      あなたの仕事適性診断

      4つの診断で、自分の適性を見つめなおそう!

      Heroes File ~挑戦者たち~

      働くこと・挑戦し続けることへの思いを綴ったインタビュー

      はじめての転職診断

      あなたにピッタリのアドバイスを読むことができます。

      転職Q&A

      転職に必要な情報が収集できます

      スカウト転職する

      企業からアプローチのメッセージが届きます。

      マイナビニュースマガジン