« HTTPのキャッシュとその関連ヘッダ | トップページ | 本当はあまり何もしてくれないDelphi-ObjectiveCブリッジ(メモリ管理編)・・・おまけ »

2013年5月22日 (水)

本当はあまり何もしてくれないDelphi-ObjectiveCブリッジ(メモリ管理編)

Delphi2010からDelphi XE4にアップグレードしましたdashdashということでさっそくFireMonkeyでMacOS XアプリというかいきなりCocoa APIを直接呼び出そうとして遊んでますが、あまりにもネット上に情報が少なすぎて死ぬ。いや、別に難しい事はするつもりはありません。普通にCocoa APIを呼ぶだけでも、色々調べると、知っておかなきゃいけない事があって面倒くせぇ・・・

というか、XE4を購入しCocoa/Objective-Cの事を調べ初めて、1週間もたってないので、用語とかうまく使いこなせないのであしからずsweat02しかも、根本的に間違ってるかもしれませんsweat01sweat01

ということで、今回は

本当はあまり何もしてくれないDelphi-ObjectiveCブリッジ(メモリ管理編)

です。

まず、基本として、Objective-CにはObjective-C Runtime という一連のただの関数群(DelphiのユニットでいうとMacapi.ObjCRuntimeに宣言されてます)があり、これを使えば、他言語とObjective-Cとのブリッジを作れるとのことです。ということで、Delphiのブリッジでも内部でこれらを使ってObjective-Cのメソッドを呼んだり、操作しています。実際どのようにブリッジしてるかは、Objective-Cのオブジェクトへのポインタがあって、それをDelphiのオブジェクトでラップして、操作する時にDelphiのインターフェース型を経由して、ラップしたObjective-Cのオブジェクトのメソッドを呼んだりしてるぐらいのイメージで当面は十分でしょう。

で、本題のメモリ管理についてです。まず、Objective-Cは主に3つのメモリ管理の仕組みをサポートしてるそうです。

  • 非自動参照カウント(MRC、Manual Reference CountingあるいはMRR、Manual Retain/Release)
  • 自動参照カウント(ARC、Automatic Reference Counting)
  • ガベージコレクション

自動参照カウントはObjective-Cコンパイラが提供してる機能で、コンパイラのサポートなしにObjective-CランタイムだけではDelphiからでは利用できません。実際は非自動参照カウントを使ってます。

で、ポイントはここです。Objective-CのオブジェクトをラップしてるDelphiのオブジェクトはDelphiのインターフェース型を経由してアクセスするので、Delphiのオブジェクト自体はコンパイラによって参照カウントが適切に増減され、参照がなくなったら適切に破棄されるのですが、Delphiのオブジェクト自体はラップしている内部のObjective-Cのオブジェクトの参照カウントつまり所有権に関してはノータッチということです。

ということで、自分で内部のObjective-Cのオブジェクトの参照カウントつまり所有権を適切に管理(retain/release)する必要があります。

具体的に見てみます。とりあえず、NSStringクラスのオブジェクトを作ってみます。

Delphiにヘルパ関数NSSTRが用意されてますが、NSSTRが内部で呼んでいるように、NSStringクラスのクラスメソッドstringWithUTF8Stringを使ってみました。内部のObjective-Cのオブジェクトの所有権を自分でretain/releaseで管理しろといっておきながらいきなりそうしてないsign02ですが、これはこれで問題ないです。というのも返されるNSStringクラスのオブジェクトの所有権を元々もってないからです。ですので、むしろ所有権が必要なければこれはそのままほっておかなければなりません(すでにautoreleaseされ、自動解放プールというものに入れられてるので適当なタイミングでreleaseメソッドが呼ばれる)。

次が問題ですが、他人様のサイトから例をとりあげます。Delphi XE2 から Mac OS X の Cocoa API を呼び出す です。

現在の日付を表示している例ですが、問題はNSDateFormatterです。NSDateFormatterクラスのオブジェクトをクラスメソッドのAllocで作成してますので、作成されたオブジェクトの所有権を既に持っています。ということで自分で、最後にreleaseメソッドを呼んで所有権を放棄しないとメモリリークを起こすよーなsign02。一方、この例ではNSDateとNSStringクラスのオブジェクトをそれぞれNSDateクラスのdateクラスメソッド、NSDateFormatterクラスのstringFromDateインスタンスメソッドから受け取ってますが、これは返されるオブジェクトの所有権をもってないので、releaseメソッドを呼んではいけません。ちなみに、上記はXE2の例ですので、XE2では問題ないのかもしれませんdash

高度なメモリ管理プログラミングガイド のCocoaにおける基本的なメモリ管理方法より

  • 自分が作成したオブジェクトはすべて自分が所有する

    オブジェクトの作成は、「alloc」、「new」、「copy」、「mutableCopy」で始まる名前のメソッ ド(たとえば、alloc、newObject、mutableCopy)で行います。

  • retainメソッドでオブジェクトの所有権を獲得できる

    一般に、(オブジェクト作成メソッドから)受け取ったオブジェクトは、そのメソッドから抜け るまで有効であることが保証されています。また、呼び出し元にそのオブジェクトを返しても安全です。retainメソッドは、(1)アクセサメソッドやinitメソッドの実装で、プロパティ値と して格納するオブジェクトの所有権を得るため、(2)他の処理の副作用でオブジェクトが無効 になるのを防ぐため(“使用中のオブジェクトの割り当て解除を避ける” (17 ページ)を参照) に使います。

  • 所有するオブジェクトが不要になったら、その所有権を放棄する

    オブジェクトの所有権の放棄は、releaseメッセージまたはautoreleaseメッセージを送ることによって行います。そのため、Cocoa用語では、オブジェクトの所有権を放棄することを一般に、 オブジェクトの「解放」と言います。

  • 自分が所有していないオブジェクトの所有権を放棄することはできない

    これは前項までの規則の帰結にすぎませんが、念のためここで述べておきます。

alloc、new、copy、mutableCopyで名前が始まるメソッドを使ってオブジェクトを作成したら所有権を持っているので、必要なくなったらreleaseして放棄しましょう。

しかし、ここまではいいんですが、ちょっと、Objective-Cのオブジェクトをしばらく保持しておこうとすると途端に死にたくなります。

ということで、Delphi側からはノータッチなのでObjective-Cのメモリ管理の仕組みを勉強する必要がありますし、最大の問題は所有権必要ならいちいちretain/releaseとかめんどくせぇだろ・・やばいよこれ・・・・

« HTTPのキャッシュとその関連ヘッダ | トップページ | 本当はあまり何もしてくれないDelphi-ObjectiveCブリッジ(メモリ管理編)・・・おまけ »

Delphi」カテゴリの記事

MacOS X」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/1497665/51716788

この記事へのトラックバック一覧です: 本当はあまり何もしてくれないDelphi-ObjectiveCブリッジ(メモリ管理編):

« HTTPのキャッシュとその関連ヘッダ | トップページ | 本当はあまり何もしてくれないDelphi-ObjectiveCブリッジ(メモリ管理編)・・・おまけ »

自作ソフトウェア

無料ブログはココログ

メモ