|
|
Log In | Not a Member? |
Contact ADC |
| < Previous PageNext Page > |
Cocoaオブジェクトは一定の寿命の間存在し続けますが、その間に、明確に区分けされた段階を経ます(少なくとも、そうした段階を経る可能性があります)。Cocoaオブジェクトは、作成され、初期化され、使用(つまり、ほかのオブジェクトからメッセージが送信)されます。また、保持されたり、コピーされたり、アーカイブされたりする可能性があり、最終的には解放され破棄されます。以下では、典型的なオブジェクトの寿命について、(現段階では)詳しいところまで立ち入らずに説明します。
最初に、最後の段階、つまり、オブジェクトが破棄される方法について説明します。ほかのプログラミング言語とは異なり、Objective-Cには、不要になった時点でオブジェクトを自動的に解放する“ガーベジコレクション”の機能はありません。ガーベジコレクションの持つオーバーヘッドや柔軟性のなさの代わりに、CocoaおよびObjective-Cでは、オブジェクトの保持と、不要になったオブジェクトの破棄を行うための、参加型の、ポリシーに基づく手続きが採用されています。
この手続きとポリシーは参照カウントの概念に基づくものです。Cocoaオブジェクトにはそれぞれ、その永続性について関心を持っているほかのオブジェクト(プロシージャコードサイトさえも含まれます)の数を示す整数が付属します。この整数のことを、オブジェクトの保持カウントと呼びます(“参照”との混同を避けるため“保持”という言葉を使用します)。allocまたはallocWithZone:クラスメソッドを使用してオブジェクトを作成するとき、Cocoaではたいへん重要な処理がいくつか実行されます。
オブジェクトのisaポインタ(NSObjectクラスの唯一のパブリックインスタンス変数)が、オブジェクトのクラスを指し示すように設定される。これにより、オブジェクトがランタイムのクラス階層の対象範囲に統合される(詳細については、“「オブジェクトの作成」”を参照)。
オブジェクトの保持カウント(すべてのオブジェクトに共通する隠しインスタンス変数の一種)が、1に設定される(ここでは、オブジェクトの作成者がその永続性に関心を持っていると仮定する)。
オブジェクトの割り当て後、通常は、オブジェクトのインスタンス変数に適切な初期値を設定してオブジェクトを初期化します(NSObjectではこの目的のために initメソッドがプロトタイプとして宣言されています)。これで、オブジェクトを使用する準備が整い、オブジェクトにメッセージを送信したり、オブジェクトをほかのオブジェクトに渡したりできるようになります。
注: 明示的に割り当てたオブジェクトと異なるオブジェクトがイニシャライザから返される場合があるため、allocメッセージ式をinitメッセージ(またはほかのイニシャライザ)の中にネストする決まりなっています。たとえば、次のようにします。
id anObj = [[MyClass alloc] init];
オブジェクトを解放すると、つまり releaseメッセージをオブジェクトに送信すると、NSObjectの保持カウントがデクリメントされます。保持カウントが1から0になると、オブジェクトの割り当てが解除されます。割り当て解除は2つのステップで実行されます。まず、オブジェクトのdeallocメソッドが呼び出され、インスタンス変数と、動的に割り当てられていたメモリが解放されます。次に、オペレーティングシステムによってオブジェクトそのものが破棄され、オブジェクトによって占有されていたメモリが回収されます。
オブジェクトを今すぐには消去したくない場合はどうすればよいでしょうか。オブジェクトの作成後にオブジェクトにretainメッセージを送ると、オブジェクトの保持カウントが2にインクリメントされます。これで、割り当て解除を発生させるには2つのreleaseメッセージが必要になります。Figure 2-3に、この単純なシナリオを示します。
もちろん、このシナリオでは、オブジェクトの作成者はオブジェクトを保持する必要はまったくありません。作成者はすでにオブジェクトを所有しています。しかし、この作成者がメッセージの中でこのオブジェクトをほかのオブジェクトに渡す必要がある場合は、状況が変わります。Objective-Cプログラムでは、ほかの何らかのオブジェクトから受け取ったオブジェクトは、オブジェクトを受け取った有効範囲の中では常に有効であると見なされます。受け取り側のオブジェクトでは、受け取ったオブジェクトにメッセージを送ったり、受け取ったオブジェクトをほかのオブジェクトに渡したりできます。このような前提を可能にするためには、送り側のオブジェクトが「行儀よくして」、送ったオブジェクトへの参照がクライアントオブジェクトによって保持されている間、そのオブジェクトを時期尚早に解放してしまわないことが求められます。
クライアントオブジェクト側において、プログラム上の有効範囲を超えた後も受け取ったオブジェクトを保持する必要がある場合は、受け取ったオブジェクトを保持できます。それには、retainメッセージを送信します。オブジェクトの保持を行うと、その保持カウントがインクリメントされ、それによってオブジェクトに対する所有者としての関心が明示されます。クライアントオブジェクトには、後でそのオブジェクトを解放する責任が課せられます。オブジェクトの作成者がオブジェクトを解放しても、クライアントオブジェクトがそのオブジェクトに対して保持を行っていれば、オブジェクトはクライアントが解放するまで存続します。この流れをFigure 2-4に示します。
オブジェクトを保持せずに、代わりにオブジェクトに対してcopyまたはcopyWithZone:メッセージを送信してオブジェクトをコピーすることもできます(ほとんどすべてというわけではありませんが、何らかの種類のデータをカプセル化しているサブクラスの多くは、このプロトコルを採用するか、このプロトコルに準拠しています)。オブジェクトをコピーすると、オブジェクトは複製されるだけでなく、ほとんどすべての場合、その保持カウントが1にリセットされます(Figure 2-5を参照)。オブジェクトの性質やその用途に応じて、コピーは浅いコピーか深いコピーになります。深いコピーでは、コピー元のオブジェクトのインスタンス変数として保持されているオブジェクトも複製されますが、浅いコピーでは、それらのインスタンス変数への参照のみが複製されます。
copyとretainの使用面における違いは、前者の場合、新しい所有者による単独使用のためにオブジェクトが要求されることです。新しい所有者は、コピー元に関係なく、コピーしたオブジェクトを変異させることができます。通常、オブジェクトを保持するのではなくコピーするのは、オブジェクトが値オブジェクトの場合、つまり何らかのプリミティブな値をカプセル化している場合です。このことは、オブジェクトがNSMutableStringなどのように可変(変異可能)の場合に特に当てはまります。不変オブジェクトの場合は、copyとretainは同等になる可能性があり、同じように実装される場合があります。
このようなオブジェクトのライフサイクルの管理方法には、実は潜在的な問題があります。オブジェクトを作成してほかのオブジェクトに渡す場合、作成元のオブジェクトでは、渡したオブジェクトをどの時点で安全に解放できるかわかるとは限りません。呼び出しスタックにそのオブジェクトへの参照が複数存在する可能性があります。そしてその一部は、作成元のオブジェクトが知らないオブジェクトによる参照も含まれている可能性があります。作成元のオブジェクトで、作成したオブジェクトを解放した後に、ほかの何らかのオブジェクトから、破棄されてしまったオブジェクトにメッセージが送信された場合は、プログラムがクラッシュする可能性があります。このような問題を避けるため、Cocoaでは割り当ての解除を遅らせる自動解放と呼ばれるメカニズムが導入されています。
自動解放では自動解放プールが利用されます(NSAutoreleasePoolクラスによって定義されています)。自動解放プールとは、明示的に定義された有効範囲内にあって、最終的に解放されるようにマークされているオブジェクトの集まりのことです。自動解放プールはネストすることができます。オブジェクトにautoreleaseメッセージを送ると、そのオブジェクトへの参照が直近の自動解放プールに置かれます。オブジェクトはまだ有効であるため、自動解放プールによって定義された有効範囲内にあるほかのオブジェクトはそのオブジェクトにメッセージを送信できます。プログラムの実行が有効範囲の終わりに達すると、プールが解放されます。その結果、プール内のすべてのオブジェクトも解放されます(Figure 2-6を参照)。アプリケーションを開発している場合は、自動解放プールを必ずしも準備する必要はありません。アプリケーションのイベントサイクルを有効範囲とする自動解放プールが、Application Kitによって自動的に準備されます。
ここまで、オブジェクトのライフサイクルについて、オブジェクトをそのサイクル全体にわたって管理するためのメカニズムを中心に説明してきました。しかし、これらのメカニズムをどのように使用するかは、オブジェクトの所有権のポリシーに応じて決まります。このポリシーをまとめると次のようになります。
オブジェクトを割り当てて初期化する([[MyClass alloc] init]など)ことによってオブジェクトを作成した場合は、オブジェクトを所有することになり、オブジェクトを解放する責任がある。この規則は、NSObjectの簡易メソッドであるnewを使用する場合にも当てはまる。
オブジェクトをコピーした場合には、コピーオブジェクトを所有することになり、コピーオブジェクトを解放する責任がある。
オブジェクトを保持した場合は、オブジェクトに対して部分的な所有権を持つことになり、不要になった時点で解放する必要がある。
逆に、次のポリシーもあります。
ほかの何らかのオブジェクトからオブジェクトを受け取った場合は、そのオブジェクトを所有していないので、解放しないこと(この規則には若干の例外があります。それらについては参考文献に明記されています)。
どのような規則にも付き物ですが、次のような例外や落とし穴があります。
クラスファクトリメソッド(NSMutableArrayarrayWithCapacity:メソッドなど)を使用してオブジェクトを作成した場合は、受け取ったオブジェクトが自動解放されるものと見なすこと。オブジェクトの解法を自分で行わないようにする。また、オブジェクトを残しておく必要がある場合は保持する。
循環参照を避けるため、子オブジェクトにおいてその親オブジェクトを保持しないこと(親オブジェクトとは、子オブジェクトの作成者か、または子オブジェクトをインスタンス変数として保持しているオブジェクトのこと)。
所有権に関する上記のポリシーに従わないと、Cocoaプログラムにおいて2つの不具合が発生する可能性があります。作成、コピー、または保持したオブジェクトを解放しなかったために、プログラムでメモリリークが発生します。あるいは、自分の管理の及ばないところでオブジェクトの割り当てが解除され、そのオブジェクトにメッセージを送ったために、プログラムがクラッシュします。そして、さらなる警告になりますが、このような問題のデバッグには長い時間がかかる可能性があります。
オブジェクトのライフサイクルの中でオブジェクトに対して発生する可能性のある、さらに別の基本的なイベントとして、アーカイブがあります。アーカイブとは、オブジェクト指向プログラムを構成している、相互に関係のあるオブジェクトの集まり(オブジェクトグラフ)を、永続的な形式(通常はファイル)に変換し、グラフ内の各オブジェクトの識別情報と関係を保つものです。プログラムを展開すると、そのオブジェクトグラフがアーカイブから再構築されます。アーカイブ(および展開)に参加するには、オブジェクトでNSCoderクラスのメソッドを使用してオブジェクトのインスタンス変数をエンコード(およびデコード)できるようにする必要があります。この目的のために、NSObjectではNSCodingプロトコルが採用されています。オブジェクトのアーカイブの詳細については、“「オブジェクトのアーカイブ」”を参照してください。
参考資料: この概要では、Cocoaオブジェクトのライフサイクルについて、説明すべきことがらのほんの一部を説明したにすぎません。メモリ管理およびCocoaオブジェクトに関連することがらの詳細については、『Objective-Cプログラミング言語』の「Objective-Cランタイムシステム」を参照してください。また、プログラミングに関するトピック『Memory Management Programming Guide for Cocoa』も参照してください。
| < Previous PageNext Page > |
Last updated: 2006-05-23
|
Get information on Apple products.
Visit the Apple Store online or at retail locations. 1-800-MY-APPLE Copyright © 2007 Apple Inc. All rights reserved. | Terms of use | Privacy Notice |