【コラム】
前回、Class構造体のobjc_ivar_listを調査することで、そのクラスが持つインスタンス変数の名前を調べることを説明した。今回は、ここで得られた名前をもとにして、インスタンス変数の値にアクセスする方法を説明しよう。
Objective-Cでのインスタンス変数のアクセス権は、だいたいほかのメジャーな言語と同じであり、@private、@proteced、@publicという3つの可視性で定義することができる。@publicを指定すれば、「->」を使って、構造体と同じようにアクセスできる。
また、クラスを構造体とみなす、@defsという指示子も用意されている。これを使うと、クラスの宣言で@protectedや@privateを使っていても、どこからでもアクセスできてしまう。たとえば、NSWindowクラスに対して、次のように@defsを使って構造体の定義を行う。
これで、NSWindowのオブジェクトを(struct ns_window*)でキャストすることで、すべてのインスタンス変数に「->」を使ってアクセスできてしまう。
だが、これらはどちらも、コンパイルの時点で変数名が分かっている場合のアクセス方法である。ここでは、実行時に動的に名前を指定して、インスタンス変数にアクセスする方法を紹介しよう。
いつものように、ランタイムAPIを使う。オブジェクトから、インスタンス変数を取得するのがobject_getInstanceVariable、設定するのがobject_setInstanceVariableだ。
第一引数が対象となるオブジェクト、第二引数はインスタンス変数の名前だ。最後の引数に、取得するときは変数のアドレス、設定するときは変数の値を指定する。どちらの場合も、あらかじめ変数の大きさに合わせてメモリを確保しておく必要がある。
ところで、このようにどこからでもインスタンス変数に好き勝手にアクセスできるとなると、不安を感じる向きもあるかもしれない。オブジェクト指向の文脈では、オブジェクトのカプセル化や、情報の隠蔽が重要な要素として挙げられることが多い。@defsやランタイムAPIは、これらのことをわざわざ壊しているのではないだろうか。
正直なところ、筆者もそのように思う。Objective-Cの言語仕様をつぶさに見ていくと、その根底にある姿勢として、「言語として制約を与える」というところが極端に少ない、と感じることがある。インスタンス変数にしても、クラスの継承関係にしても、実行中に触り放題である。
このような言語では、オブジェクト指向の方法論として語られる情報隠蔽を実践するのは、言語レベルではなく、プログラムレベルということになるだろう。Objective-Cは、言語としてクラスやメソッドといったオブジェクト指向の道具立てを用意するが、それはかなり自由に使える状態にある。プログラマはそれを使い、ある箇所では継承やカプセル化に基づく抽象度の高いオブジェクト指向でプログラミングし、ある箇所ではスピードを最大限に出すためC言語に近いプログラミングをする。
このような使い分けができる訳である。この辺りが、Objective-Cが自由で動的な言語と呼ばれることもあれば、いいかげんな言語と揶揄されたりすることもある所以であろう。
では、ランタイムAPIを使ってインスタンス変数にアクセスする例を紹介しよう。
Cocoaでは、ウィンドウを表すNSWindowというクラスがあり、このオブジェクトからはビューのルートをcontentViewで取得できる。このルートには、実はFrameViewという呼ばれるさらに親がある。FrameViewは、titleCellというインスタンス変数を持ち、これはウィンドウのタイトルを表示するときに使われるセルである。
そこで、このtitleCellを取得して、ウィンドウのタイトルに使われているフォントを変更してみよう。
titleCellはヘッダには定義されていないので、object_getInstanceVariableを使って動的に取得している。このコードの動作結果は、次の図のようになる。
|
|
ウィンドウタイトルのフォントを変更 |
このコードの優れている点は、インスタンス変数を動的に調査しているため、仮にtitleCellというインスタンス変数が無くとも、クラッシュはしないことである。もちろん、フォントの変更は機能しないが、クラッシュしたり起動できなかったりという、最悪の事態は避けられるのである。
今までの流れでいくと、次はインスタンス変数の動的な追加ということになる。しかし、これは大きな問題をはらむ。
理屈としては、追加することは可能である。新しいobjc_ivar_listを作成して、Class定義で置き換えてやればいい。だが、これにはいくつかの問題がある。
1つは、インスタンス変数の宣言は、オブジェクトをallocしたときのサイズを定義している点である。仮に動作の途中でインスタンス変数の宣言を追加したとしても、すでにインスタンス化されたオブジェクトが確保しているメモリは増えない。したがって、同じクラスのインスタンスなのにサイズが違うという、つまり追加したインスタンス変数を持つものと持たないものがあるという、危険な状態が生じる。
もう1つは、継承関係の途中になるクラスに追加するときである。インスタンス変数を宣言するときは、その変数がメモリ上のどこに確保されるか、オフセットを指定しなくてはいけない。通常、サブクラスは親クラスと被らないようにオフセットを割り当てていく。だが、親クラスでインスタンス変数を追加すると、サブクラスの変数と被ってしまう危険が出てくる。
このようなことを考えると、インスタンス変数の動的な追加は、まだインスタンスを作成しておらず、継承関係の一番最後、つまりサブクラスが存在しないクラスに限る、ということになってしまう。これではあまりに限定された使い方になってしまうので、この記事ではインスタンス変数の動的な追加はお勧めしない、ということを述べるにとどめておこう。
| トマトを食べれば痩せられる!? -京大ら、新発見の成分で肥満改善効果を実証 [21:00 2/10] |
| JAXA、液体シリコン中に残存する共有結合を観察 -大口径ウェハの実現に期待 [20:11 2/10] |
| NEDOなど、熱膨張が小さな樹脂複合材料ペレットの量産化に成功 [19:22 2/10] |
| 理研、一般顕微鏡を蛍光顕微鏡に強化できるアダプタを試作して性能を実証 [19:15 2/10] |
| 天の川のブラックホールが小惑星を飲み込んでいる - NASAが発表 [18:08 2/10] |
|
【レポート】「jdk BAND」メンバーや「みっしぃ」も登場! 『英雄伝説 零の軌跡 Evolution』制作発表会 [02:54 2/11] ホビー |
|
【レポート】人気の無料/有料アプリを毎週紹介 - 1月31日~2月8日のAndroidアプリランキング [01:00 2/11] 携帯 |
|
渡部篤郎主演でドラマ化!地元新聞社が伝え続けた東日本大震災の物語 [00:08 2/11] キャリア |
|
沖縄発ヒーロー革命!「琉神マブヤー」が海を越えハワイを目指す! [00:08 2/11] キャリア |
|
中国四川省、パンダの保護と生態研究の最新情報とは? [00:08 2/11] キャリア |