viva Cocoa Objective-C 入門 第3章 Objective-C
ホーム メール
 
目次 < 前ページ 次ページ >

オブジェクト

 ここからはObjective-Cに限定した話しになります。他のオブジェクト指向プログラミング言語にはあてはまらない部分も含んでいます。

オブジェクト

 オブジェクトとは白くて大きな箱みたいなものです。白いというのは私の単なるイメージです。仮に黒い箱であっても青い箱であっても構いません。この箱は中を見ることはできませんが外側に操作ボタンが一杯ついています。このオブジェクトがどのような働きをするのか、どのボタンを押せばどのようなことが起こるのかはリファレンスと呼ばれる取扱説明書に書かれています。

「箱の中をみることはできない」
 このことをカプセル化(隠蔽化)と呼びます。カプセル化については後ほど説明いたします。


「リファレンス」
 残念なことに現在Appleから提供されているリファレンスは英語だけとなっています。

 この白い箱の中には十数個の変数と呼ばれるデータの入れ物と、数十個のメソッドと呼ばれる実際に作業をする“人”が入っていて、人ひとりはひとつのメソッドにしか対応できない事とします。外側のボタンはこのメソッドに仕事を命ず(じ)るボタンです。しかしボタンの数だけ中に人が入っているとは限りません。押される頻度の少ないボタンについては自社で人を雇うよりも外注に出したほうが得であるという経営学がここでも活きています。そこでそれらの作業については「ターゲットアクション」、「デリゲート」、「データソース」などと呼ばれる外注先に作業を依頼するようになっています。

「経営学的に得」
 比喩で言っているわけではありあません。オブジェクトは規模が大きくなればなるほどメモリの占有率も増えます。CPUの負荷も増えます。このようにリソース(資源:メモリやCPUのパワー)を多く使うことを「コストが高い」と言います。そこでObjective-Cではできるだけコンパクトにオブジェクトをまとめるように工夫されています。

 なおこのオブジェクトには実際にはボタンなどという不細工なものはついていません。ボタンのないMighty Mouseのようにツルッとしています。そしてボタンを押すかわりにオブジェクトに対してメッセージを送信して「○○というメソッドを実行してください」と依頼をします。このことはLANケーブルをなくしたAir Macのようなスマートな方法です。

 これに対してC++などではオブジェクトのボタンにスイッチを押すためにLANケーブルが繋がっているような形になっていています。そしてC++ではこれを「無線化」をしようとする動きはまったくありません。なぜならLANケーブルにはLANケーブルなりの良さもあるわけです。例えば有線LANでは秒間1ギガビットでのデータ転送(論理値)ができますが、無線LANでは百数十メガビットでのデータ転送(論理値)が現段階では限界です。

 このデータ転送速度についても単なる比喩ではありません。実際にボタンを押した後の反応速度はObjective-CよりもC++のほうが速いのです。しかし無線LANは一度使うとやめられません。このことと同じように多少のパフォーマンス(実行速度)が犠牲になったとしてもよほどの理由のない限り無線LANをやめようとは思わないでしょう。

「無線LAN」
 なぜObjective-Cがこのようなことができるのか。その答えはランタイムシステムにあります。ランタイムシステムについても後ほど説明いたします。

オブジェクトの構造

 私が初めて読んだC++の解説書は林晴比古著の改訂版「新C++言語入門ビギナー編」だったと思います。Objective-Cという言語の存在を知る前のことでした。そしてそこには「オブジェクトとは構造体に、それに関係している関数を付け足して一緒にしたものと考えてください」という意味の説明されていました。本当はもっと正確に引用しなければいけないのですが、すでに私はこの本を持っていません。またこの本じたいも新訂版に変わっています(説明文も変わっています)。ところが最近、2002年に発刊されて今でもCocoaプログラミングの良書として知られているアーロン・ヒレガス著の「Mac OS X Cocoa プログラミング」にも同じように構造体から発展させてオブジェクトの説明をしている下りあることに気付きました。一度というか何度か読んでいるのですが久しぶりに読んでみて気づきました。大変分かりやすい説明だと思いますので次に引用します。

アーロン・ヒレガス著 村上雅章訳「Mac OS X Cocoaプログラミング」P4〜P5

 「オブジェクトとは何でしょうか?オブジェクトは、メモリ中に確保され、その内部に変数を保持するという点で、Cの構造体と良く似ています。オブジェクトの中の変数はインスタン変数と呼ばれます。 〜中略〜 しかし、オブジェクトには構造体よりも優れている点があります。つまり、オブジェクトにはそれと関連付けられた関数を保持できるのです。こういった関数は、メソッドと呼ばれます。そして、該当オブジェクトに対してメッセージを送信することによって、メソッドを呼び出すことができるのです。」


 林晴比古氏の説明はC++でのオブジェクトの説明です。そしてアーロン・ヒレガス氏の説明はObjective-Cにおいてのオブジェクトの説明です。この大御所の2人が違うオブジェクト指向プログラミング言語において揃って同じ説明をされていることは少し嬉しい発見です。これ以上の説明はもはや不必要かと思います。

 ただしこの2人の大御所の説明は歴史的に見れば間違いです。構造体はC++の前身のC言語で登場してきたデータ構造です。そして実際にはC言語の少し前にレコードという名称でこのデータ構造は登場しています。
 それに対して世界初のオブジェクト指向言語であるSmalltalkはさらにそれ以前に登場しています。つまりオブジェクトの登場は構造体(レコード)よりも先なのです。しかしそれらのことを踏まえた上で敢えて便宜的に前述のような説明をされているのは明白です。そして実際にこの説明は分かりやすいものです。

理解の助けになるように各言語におけるオブジェクトの構造を図示します。

図 各言語のオブジェクトの構造

 一番左がCの構造体になります。各言語の白い長方形がデータの入っている変数です。Cの構造体ではこの変数の一つ一つをメンバ( member )と呼びます。左から2番目からは、順番にC++、Java、Objective-Cのオブジェクトを表しています。構造体と同じように白い長方形は変数部分です。C++ではメンバ変数、Javaではフィールド、Objective-Cではインスタンス変数と呼びます。墨色の部分はそのオブジェクトの動作や振る舞いを定義している部分です。それぞれにメンバ関数、メソッドなどと呼びます。またC言語での「メンバ」という呼び名が親しまれているので他の言語でも変数部分だけでなく関数部分やメソッド部分も「メンバ」と呼ぶこともあり、またそれでも話しは通じます。

 この図はウィンドウを表す構造体もしくはオブジェクトだと仮定します。Cの構造体、および各オブジェクトの変数には、上から順番にウィンドウの横幅(整数値)、ウィンドウの高さ(整数値)、ウィンドウのタイトル(文字列)が格納されていることにします。そしてC++以降のオブジェクトでは墨色の関数やメソッド部分でウィンドウのクローズボタンが押された時の動作などが定義されていることします。以上がだいたいのオブジェクトの構造です。

 ただしこの図はあくまでも説明しやすいように簡略化したオブジェクトの概念図です。実際の開発環境ではこのウィンドウオブジェクトは呼び名こそ違え必ずAPIもしくはフレームワークとして準備されています。そして変数としておそらく十数個、関数やメソッドとしては数十個が用意されています。またクラッシック時代のMacでは“windowRecord”というウィンドウのためのデータを集約できる構造体がAPIとして用意されていました。レコード(record)とは先に説明したように、C言語の少し前に登場した、構造体のように違う型の複数の変数を一つにまとめたデータ構造のことです。プログラミングの世界ではこのレコードという呼び方のほうが一般的です。構造体というのはあくまでもC言語の中でのレコードの呼び方になります。

上記のwindowRecordは正式名称ではありませんが、およそこういう名称でした。

オブジェクトのカプセル化

 オブジェクト指向プログラミングではオブジェクトのデータ(メンバ変数、フィールド、インスタン変数)には、ほかのオブジェクトからは直接アクセス(読み書き)できないほうが良いとされています。

 Objective-Cではデフォルトでインスタン変数にprotectedというアクセス指定がかかっています。protected指定されたインスタン変数はそのインスタン変数を宣言しているオブジェクトか、もしくはそのサブクラスのオブジェクトでなければ直接アクセスすることができません。

 それ以外のオブジェクトからはアクセサメソッドと呼ばれるメソッドを使ってインスタン変数の値を設定したり取得したりすることになります。


■ インスタン変数およびメソッドの命名規則

横道にそれますが、ここでインスタンス変数とメソッドの命名方法について説明します。


■ アクセス指定子

 他のOOPにあわせて「アクセス指定子」というタイトルを付けましたがObjective-Cの場合にはアクセス・コンパイラディレクティブと呼ぶほうが正しいのかもしれません。
 Objective-Cでは通常のC言語ではない部分、つまりObjective-Cのコード部分には@マークを付けて区別しています。<▽コメント>区別していることを前章で少しだけ説明しました<△コメント>。この@マークの付いたキーワードの多くはコンパイラディレクティブ(コンパイラ指示子)と呼ばれています。コンパイラ(処理系)に指示を出すという意味になります。そして、アクセス指定に関するコンパイラディレクティブは次のようになります。

@public
 このコンパイラディレクティブ以降に宣言されたインスタン変数はすべてのオブジェクトからアクセスすることができます。

@private
 このコンパイラディレクティブ以降に宣言されたインスタン変数はこのインスタンス変数を宣言したオブジェクト以外からはアクセスすることができません。アクセスを許可されていないオブジェクトからこのインスタン変数の値を設定・取得する場合には次に説明するアクセサメソッドを使わなければなりません。

@protected
 このコンパイラディレクティブ以降に宣言されたインスタン変数はこのインスタンス変数を宣言したオブジェクトとそのサブクラスのオブジェクト以外からはアクセスすることができません。アクセスを許可されていないオブジェクトからこのインスタン変数の値を設定・取得する場合には次に説明するアクセサメソッドを使わなければなりません。
 なおObjective-Cではこのprotectedアクセス指示子がデフォルトとなっています。なにもアクセス指定されていない場合にはこのprotectedが指定されていることになります。

 これらのアクセス指定のコンパイラディレクティブがコードの中に登場した場合には、それ以降のインスタンス変数は、違うコンパイラディレクティブが登場するまでは同じアクセス指定になります。

 @マークがついていてもコンパイラディレクティブでない場合もあります。しかしそこがC言語ではなくObjective-C固有のコードであることを表すために使われていることに変わりはありません。

 このことから類推すると(考えると)「アクセス指定はObjective-C固有のもの?」ということになります。そのとおりです。C言語にはアクセス指定という概念はありません。

 またObjective-CにはC言語ではない部分を表すもうひとつの記号があります。それはメッセージ式と呼ばれる [ ](角括弧もしくは大括弧と呼びます)で囲まれた部分です。@マークか [ ](角括弧)が現れたら、そこはObjective-Cで記述されているのだと思ってください。

 ただし [ ] はC言語の配列を表す場合にも使います。Objective-Cのメッセージ式なのかC言語の配列なのかはその文脈から判断してもらう必要がありますが、実際にはまず間違うことはないだろうと思います。なおメッセージ式については次項で説明いたします。


■ アクセサメソッド

 インスタン変数の値を設定するメソッドをセッター(setter)メソッド。インスタン変数の値を取得するメソッドをゲッター(getter)メソッドと呼びます。そしてこの2つメソッドをあわせてアクセサメソッドもしくは単にアクセサ(accesser)と呼びます。アクセサメソッドには独特の命名規則があります。たとえば、インスタン変数の名前がvariableの場合は次のように命名します。

セッターメソッドは setVariable になります。
インスタン変数の名前の前にsetを付けてインスタン変数名の最初の文字を大文字に変えます。

ゲッターメソッドは variable になります。
インスタン変数の名前をそのままをゲッターメソッドの名前にします。

 Java言語などではゲッターメソッドはセッターメソッドと対比させて getVariable と命名することになっています。しかしObjective-Cではこの「インスタンス変数の名前をそのままゲッターメソッドの名前にする」ことを利用したプログラミング手法があります。必ずこの命名規則を守るようにしてください。

 以上のようにあるオブジェクトのインスタン変数の値を他のオブジェクトから設定・取得する場合には実質的にはアクサセメソッドを用いなければなりません。protectedの場合はスーパークラスのインスタンス変数には自由にアクセスできることになっていますが、スーパークラスの各メンバ(インスタンス変数)は継承した時点でそのクラスに含まれているメンバ(インスタンス変数)と同じことになります。この実質的に自身のインスタンス変数と同じ扱いになるスーパークラスのインスタンス変数へのアクセスを禁止する@privateが使われた場合は非常に窮屈なプログラミングをしなければならないことになります。このように他のオブジェクトのインスタン変数に直接アクセスできないことをオブジェクトのカプセル化(隠蔽化)と呼びます。

 なおオブジェクトが自身のインスタン変数に対して自身のアクサセメソッドを使うこともできます。そしてまたそのようにコーディングされているサンプル例も多く存在しています。

 すでにJavaなどのオブジェクト指向プログラミングを経験されている場合は別として、このアクセサメソッドという概念はOOPがはじめての方には難しいものだと思います。なぜならC言語には登場してこないからです。
 次章からは早速アプリケーションを作っていきます。それこそ習うより慣れろです。今の段階ではあまり難しく考えずにとりあえず作っていきましょう (進めていきましょう) 。


■ カプセル化のメリット

 例えばある生徒の国語のテストの点数を格納するインスタン変数があるとします。通常テストの点数は0点から100点です。ところが先生が間違って120あるは-20と入力してしまった場合には正常なテストの点数の範囲を守るために101以上の点数は100に、-1以下の点数は0としてインスタン変数に点数を格納するようにセッターメソッドを定義しておくことができます。

 あるいは、もともと120や-20が100もしくは0の入力間違いとは限りません。テストの点数として不正な値が入力された場合には先生にテストの点数としてあり得ない数字を入力したことを伝え、再度正常な範囲内の点数を入力することを促すようにセッターメソッドを定義することもできます。

ランタイムシステムとメッセージ式


■ ランタイムシステム

 この節の最初でC++は有線LANでObjective-Cは無線LANだ。というたとえ話をしました。ではなぜObjective-Cは無線LANのような器用なことができるのでしょうか。それはObjective-Cでコーディングされたプログラムはランタイムシステムという通常とは少し違うプラットフォームで動作しているからです。もっとはっきりと言えばランタイムシステムとはJavaのJVM(ジャバ・バーチャル・マシン)と同じ仕組みになっています。すなわちObjective-Cプログラムを実行させるためのバーチャルマシンをMacの上に構築して実行しているわけです。この仕組みのおかげで柔軟なメソッド呼び出しができてしまうわけです。

 「でも、たしかにC++プログラムよりはパフォーマンスは悪いのかもしれないが、Pure Javaに比べれば遥かに良いパフォーマンスではないですか」と感じられている方も多いと思います。それはObjective-CのランタイムシステムはあくまでもMacオンリーで使うことを目標に設計されているからだと思います。それに比べてJavaは色々なリアルマシンに対応しなければなりません。この差は大きいのではないかと思います。

 ではなぜObjective-Cは本当の意味でMacネイティブになっていないのでしょうか。そのことについてはSmalltalkのところまで話しを戻さなければなりません。

 ALTOコンピュータではじめて実現されたGUIシステムはSmalltalkというインタプリタ型のプログラミング言語で動作しました。当時はGUIシステムを実現するにはインタプリタ型でないと無理だと考えられました。というかそうでないとGUIを構築するコードだけで大変なことになる、と考えられたのだと思います。そしてこのことは事実です。

 OS Xに移行する直前のMacプログラムはそのほとんどがC++で書かれていました。そして(次のことは)一度経験すると分かってもらえると思いますが、C++のような純粋なコンパイラ型の言語でGUIを構築するのは大変なことです。(“CodeWarrior”の“Power Plant”という優秀な開発環境を使っても大変なことでした。というかそもそもC++じたいが難しい :-))

CodeWarrior(コードウォーリア)

Metrowerks社が発売する統合開発環境(IDE)。当初Macintosh用として開発されましたが、その後、あらゆるプラットフォーム用に移植され、現在でもゲーム機用プログラミングでは活躍しています。Power PlantはXcodeでのInterface Builder的存在にあてはまります。
 なお、Apple社のXcode無料配布と主力言語のObjective-Cへの移行に伴い、現在はMac版の発売と開発は行っていません。

 ということで、本書でのランタイムシステムについての説明はこれで終わります(唐突ですがこれで終わりにします)。なぜなら、例えばJavaにはJVMについての詳しい説明書が付いています。しかしJava言語入門者がこのJVMの説明書を読むことはまずないでしょう。それと同じでObjective-C入門者が裏方のランタイムシステムについて熱心に学習することはあまり考えられないからです。それよりは先にすすみましょう。

 Objective-Cで書かれたプログラムはC++で書かれたプログラムよりパフォーマンスが悪い(実行速度が遅い)と書きましたが、それは事実です。しかしその差は体感できるようなものではありません。実験すれば分かるという程度です。しかも遅れをとるのは初回のメッセージ送信の時だけです。したがって作業全体を通しても差を感じることはまずないでしょう。


■ メッセージ式

 ランタイムシステムについての説明は終わりますと言いましたが、ランタイムシステムによってObjective-Cが受ける恩恵については話しを続けます。

 あるオブジェクトのメソッドを実行したい時にObjective-Cではメッセージ式というものを使います。このメッセージ式はObjective-Cの代表的な特徴のひとつですが、ここでは詳細を述べるのではなく実際の使用方法について説明していくことでメッセージ式の説明とさせていただきます(もらいます)。なぜなら詳細の説明を行うためにはまたランタイムシステムの話に戻らなくてはならないからです。

 メッセージ式は [receiver message] という形になっています。[ ](角括弧もしくは大括弧)で囲まれた左側のreceiver(レシーバ)にはオブジェクトのクラスやインスタンスの代入された変数、もしくは結果としてインスタンスが返ってくる式を記述します。つまりオブジェクトを指定するということになります)。
<▽コメント>
「ということになります」という言い回しは使わないほうが良いとのことでしたが、ここでは敢えてこの言い回しにしました。なぜなら他言語ではクラスはオブジェクトとして扱えないのに、Objective-Cでは扱えるという違和感を表現したかったからです。しかし言い回しがオカシイと思われた場合は遠慮なく校正してください。
<△コメント>

「つまりオブジェクトを指定するということになります」

 ここで重要なことはクラスもオブジェクトであるということです。Objective-Cではクラスとインスタンスの両方がオブジェクトになります。このことはObjective-Cの重要な特徴のひとつです。C++などではインスタンスだけがオブジェクトでクラスをオブジェクトとして扱うことはできません。
 しかしObjective-Cでも、ガイドブックなどで「オブジェクト」と記述されている場合はインスタンスのことを指している場合が多いみたいです。実際にどちらを指しているかは、文脈から判断していただくしか仕方ありません。

 メッセージ式の角括弧で囲まれた右側のmessage(メッセージ)の部分にはレシーバが持っていると思われるメソッド名を記述します。正確には“selector”と呼ばれるレシーバのオブジェクトがメソッドを識別するための識別子がmessageになるのですが、通常の場合はメソッド名を記述することでselector(セレクタ)を記述したことになります。あとはレシーバのオブジェクトがそのメソッド名からselectorを割り出して実行するという仕組みになっています。

「レシーバが持っていると思われるメソッド名を記述します」

 この点もObjective-Cの大きな特徴のひとつです。C++では必ずオブジェクトが持っているメンバ関数を呼び出さないとコンパイル時にエラーになります。しかしObjective-Cではコンパイル時にオブジェクトがそのメッセージに応えられるかどうかは問わないないことになっています。そして実行時になってそのメッセージに応えられないことが分かっても必ずしもエラーに発展するとも限っていせん。何も起こらずにただ無視されるだけということもあります(当然、実行時エラーになることもあります)。
 ただしレシーバの型がはっきりとしている場合にそのクラスに存在しないメソッド名をメッセージにしている事がコンパイル時に分かった場合には警告は出ることになっています。

 なお、このことについては「動的結合」のところでもう一度説明いたします。


■ クラスメソッドとインスタンスメソッド

 ここで注意しなければならないことは、レシーバがクラスならばメッセージはクラスメソッドにしなければならないことです。そしてレシーバがインスタンスならばメッセージはインスタンスメソッドにしなければなりません。

 少し話しが外れますが、メッセージ式はネスト(入れ子)にすることができます。ネストの例として最も良く見かけるものは次のコードになるでしょう。

          variable = [[AClass alloc] init]; 

 この式は [AClass alloc] でAClassのインスタンス変数をメモリに確保します。この時点でAClassはインスタンス化(実体化)されたことになります。もう一度言いますがメモリに新たに確保されるのはインスタンス変数だけです。メソッドについてはそのクラスのすべてのインスタンスが共用(共有)するかたちで1セットだけがメモリに確保されます。メソッドがメモリに確保(ロード)される時期(タイミング)はまちまちだとしか言えません。Cocoaフレームワークで定義されているクラスの場合はOS X を起動した時か、もしくはそのクラスが初めて使われる時にメモリにロードされます。アプリケーションで独自に作成したクラスの場合はそのアプリケーションを起動したときか、もしくはそのクラスをはじめて使うときにメモリにロードされます。

 話しを戻します。allocでインスタンス化されたオブジェクトは次のメッセージ式で初期化されます。

          [インスタンス init]

 そしてこの初期化されたインスタンスのアドレスが戻り値として変数variableに代入されます。この例の場合 alloc はクラスメソッドで init はインスタンスメソッドです。それぞれの「メソッド宣言」は次のようになっています。「メソッドの宣言」については次節の「クラス」であらためて説明しますが、C言語の「プロトタイプ」と同じものだと思っていただいて差し支えありません(問題ありません)。

     + (id)alloc;

     - (id)init;

 このように先頭に + (プラス記号)が付いているものがクラスメソッドになります。

 それに対して先頭に - (マイナス記号)が付いているものがインスタンスメソッドになります。そしてこの + と - の記号のあとに( )で囲まれてidという文字があります。これが戻り値の型になります。Objective-Cでは戻り値や引数の型は( )で囲むことになっています。id型はオブジェクト型とも呼ばれ、すべてのオブジェクトの型として使える汎用の型になります。id型には *(アスタリスク)は付いていませんが実際にはポインタになっています。Objective-Cではオブジェクトを表す変数はすべてポインタにすることに決まっています。

 なお、このメッセージ式を C++ や Java のメンバ関数呼び出しやメソッド呼び出し風に記述すれば次のとおりになります。参考にしてください。

アロー演算子の場合
          temporary = AClass->alloc;

          variable = temporary->init;
 もしくは
          variable = AClass->alloc->init;

ドット演算子の場合
          temporary = AClass.alloc;

          variable = variable.init;
 もしくは
          variable = AClass.alloc.init;

Objective-Cのメッセージ式に慣れるまではかなり違和感を感じるかもしれません。しかし慣れれば面白くも感じてくるでしょう。


■ 動的結合(dynamic binding)

 ところでObjective-Cのメッセージ式には C++ などのメンバ関数呼び出しとは見かけ以外にも根本的に違う部分があります。Objective-Cではnilポインタ、つまりオブジェクトが代入されていない変数にメッセージを送ることができます。当然メッセージを送っても何も起こりません。しかしエラーも起こりません。Objective-Cのコンパイルではメッセージ式のレシーバの変数の型がid型であればその先まではチェックされません。ようするにそのメッセージ式が確実にメソッドを実行できるかどうか分からないままコンパイルされ実行ファイルができあがります。実際にメッセージ式が有効かどうかはアプリケーションが起動してそのメッセージ式が実行されるまで分かりません。これはとんでもないことのように思われるかもしれませんが実行時にレシーバのオブジェクトや実行するメソッドを変更することができる。という柔軟性を持ち合わせていることになります。このように実行時にメソッドの結合が決定することを動的結合(dynamic binding)と呼びます。これに対してC++(やJava)のようにコンパイル時に関数やメソッドへの結合が確定しているものを静的結合(static binding)と呼びます。なおこの動的バインディングはコーディングに柔軟性を与えますが、(何度か言っているように)実行速度が少し遅くなるというデメリットもあります。しかしMacやiPhoneがObjective-Cを採用していることからも分かるとおりそのデメリットは、おそらく人が感知することができない程度の微々たるものです。多くの携帯電話ではJavaが使用されていますがObjective-Cを採用しているiPhoneのパフォーマンスの良さ(実行速度の速さ)はiPhoneを使われたかたはすでに実感されていることだろうと思います。

 なお今後も「メソッド呼び出し」という表現を使う場合もありますが、実際には「メッセージ送信」のことだということを覚えておいてください。また「オブジェクト(あるいはインスタンス)を代入する」、もしくはオブジェクト(あるいはインスタンス)を返す」という表現も使いますが、Objective-Cではオブジェクトは常にポインタでやり取りされる決まりになっています。したがってこの表現も正確には「オブジェクト(あるいはインスタンス)のポインタを代入する」、もしくは「オブジェクト(あるいはインスタンス)のポインタを返す」という意味であることも覚えておいてください。

 Objective-Cではコンパイル時に静的チェックがおこなわれないと書きましたが、実際には戻り値や引数やレシーバの型がid型でない場合には、そのオブジェクトがメソッド(メッセージ)に応答できるかどうかチェックされます。そして間違っていれば(応答できないことが分かれば)警告も出ます。しかし動的結合を謳っているせいかエラーにはならず実行ファイルは作成されます。しかし実際にはその実行ファイルは起動できない場合もあります。確かにSmalltalkの頃には「型の存在しないプログラミング言語」という表現がされていたみたいでかなりの動的結合ができたのでしょうが、今のObjective-CではAppleからid型の使用を控えるようにという指針も出ていて動的結合をできるだけ抑えるような動向があるように感じます。抑えるというよりは「多用は避けてください」あるいは(もしくは)「不必要に使わないでください」ということだと思います。実行時にオブジェクトやメソッドを入れ替えられるというのは確かに魅力的ですが、実際にそれを利用したアプリケーションとはどういうものがあるだろうかと考えると、そうそう思い浮かばないのも事実だと思います。


 動的結合の概念は結構難しい話しだと思います。そこで再び無線LANのたとえ話でもう一度説明いたします。

 有線LANでルータに接続していた場合、当然その接続を変更することはできません。できることはLAN経由の接続先を違うプロバイダに変更したりすることぐらいです。ところが無線LANはひとつの子機から複数の親機を選択・変更することができます。「あ!」と思われたかもしれませんが、これが動的結合で得られるメリットとほぼ同じことになります。ここで「接続する親機を変更できる」というのは「メッセージ式のレシーバを変更できる」という事同じ事になります。

( なお、このたとえ話ではLANケーブルを引き抜いて違うルータに接続しなおしたり、PCが複数のLANケーブルでの接続をしたりしている場合などは想定していません。それはもうハードウェアの交換や追加と同じことになり、論点が少し違ってきていることだと思います。)

オブジェクトの比較

 次のようにオブジェクトの代入された変数aVariableとanObjectを等価演算子で比較するとします。

          aVariable == anObject

 この場合に等号か不等号かを比較されているのは両方の変数が同じオブジェクトを指しているポインタかどうかという点です。つまり本当に同じオブジェクトなのかどうかを判定しています。この比較をするには等価演算子だけではなく、次のメソッドを使うこともできます。

          - (BOOL)isEqual:(id)anObject

 このメソッドはすべてのオブジェクトに用意されていますが、これをメッセージ式に使うと次のようになります。

          [aVariable isEqual:anObject];

 このメッセージ式はレシーバaVariableとメッセージisEqual:の引数anObjectが同じオブジェクトを指すポインタだった場合にBOOL型のYESを返します。BOOL型は真偽値を表すためにchar型をtypedefした型でObjective-Cだけで使えてC言語の中では使えません。真を表すYESは整数1の記号定数、偽を表すNOは整数0の記号定数になっています。

 しかし値の比較という意味では同じポインタすなわち同じオブジェクトかどうかを比較したいのでなく、同じ値を表しているオブジェクトかどうかを比較したい場合もあります。たとえば文字列を保持する2つのオブジェクトstringAとstringBが保持している文字列が同じ @"ABCD" であるかを比較したい場合などがあります。このような場合には前述のisEqual:メソッドをそのような比較をするように上書きしてメソッドの定義を変更することができます。「上書き(override)」とは継承したメソッドの実装(内容)を変更することです。

 と、説明は続きますが、そろそろ話しが難しくなってきたことだと思います。というかそろそろオブジェクトの素(設計図)であるクラスについて具体的に説明するときがきたみたいです。節をあらためて「クラス」の説明に移りたいと思います。

Column @"ABC"というオブジェクト定数

 C言語では文字列を表すのに "ABCD" と記述していました。Objective-CではObjective-C固有のことには先頭に@マークをつけるという規則どおりに @"ABCD" と記述します。そしてこの文字列はNSStringというクラスのオブジェクトになります。@"ABCD"と記述するだけで文字列ABCDを保持するオブジェクトができるのですから便利な記述方法です。しかしこの記述方法で作成された文字列はオブジェクト定数というものになります。

 定数という呼び方からも分かるとおりこの文字列は変更することはできません。変更できないだけなら良いのですが、オブジェクト定数は必要でなくなっても削除することができません。つまり一度作成されたオブジェクト定数は、そのアプリケーションが終了までメモリ領域に残ることになります。

 最近のMacは、はじめから大容量のメモリを搭載しています。ですからこのような微々たるメモリの無駄は気にしなくても良いのかもしれません。しかしオブジェクト定数は不必要になったらメモリを無駄に消費しているだけ。ということは覚えておいたほうが良いでしょう。

 なお、通常の方法で作成されたNSStringクラスのオブジェクト(インスタンス)はしかるべき手続きを踏めばメモリから削除することができます。



目次 < 前ページ 次ページ >


Copyright 2006 - 2010 viva Cocoa. All Rights Reserved.