これをできるだけ簡潔に書き留めようとしていますが、説明するのは簡単ではありません-読んでくれてありがとう=)
私は、オープン ソース iPhone フレームワークSparrowの主な開発者です。Sparrow は Flash AS3 ライブラリをモデルにしているため、AS3 と同様のイベント システムを備えています。現在、そのシステムはセレクターを指定することで機能しますが、イベント リスナーにブロックを使用できるようにすることで、そのシステムを拡張したいと考えています。ただし、メモリ管理の問題につまずいています。
現在処理されているイベントの典型的な使用例を紹介します。
// init-method of a display object, inheriting from
// the base event dispatcher class
- (id)init
{
if (self = [super init])
{
// the method 'addEventListener...' is defined in the base class
[self addEventListener:@selector(onAddedToStage:)
atObject:self
forType:SP_EVENT_TYPE_ADDED_TO_STAGE];
}
return self;
}
// the corresponding event listener
- (void)onAddedToStage:(SPEvent *)event
{
[self startAnimations]; // call some method of self
}
これは非常に簡単です。オブジェクトが表示リストに追加されると、イベントを受け取ります。現在、基本クラスはイベント リスナーを NSInvocation-objects の配列に記録します。NSInvocation は、そのターゲットと引数を保持しない方法で作成されます。(ユーザーはそれを行うことができますが、99% の場合は必要ありません)。
これらのオブジェクトが保持されないのは意識的な選択でした: そうしないと、ユーザーが dealloc-method でイベント リスナーを削除したとしても、上記のコードによってメモリ リークが発生します! 理由は次のとおりです。
- (id)init
{
if (self = [super init])
{
// [self addEventListener: ...] would somehow cause:
[self retain]; // (A)
}
return self;
}
// the corresponding event listener
- (void)dealloc
{
// [self removeEventListener...] would cause:
[self release]; // (B)
[super dealloc];
}
一見すると、これで問題ないように見えます。init メソッドの保持は、dealloc メソッドのリリースと対になっています。ただし、保持カウントがゼロにならないため、dealloc メソッドが呼び出されることはないため、これは機能しません。
私が言ったように、「addEventListener...」メソッドは、まさにこの理由で、デフォルト バージョンでは何も保持しません。イベントの仕組み (ほとんどの場合、イベントは「自己」または子オブジェクトによってディスパッチされ、とにかく保持されます) のため、それは問題ではありません。
しかし、ここで問題の中心部分に行き着きます。ブロックではそれを行うことはできません。私が望むように、イベント処理のブロックバリアントを見てください:
- (id)init
{
if (self = [super init])
{
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[self startAnimations];
}];
}
return self;
}
それは見栄えがよく、非常に使いやすいでしょう。ただし、ユーザーが「self」でメソッドを呼び出したり、ブロック内でメンバー変数を使用したりすると (ほとんどの場合)、ブロックは自動的に「self」を保持し、オブジェクトの割り当てが解除されることはありません。 .
今、私は、次のように自己への __block 参照を作成することで、任意のユーザーがこれを修正できることを知っています:
__block id blockSelf = self;
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
[blockSelf startAnimations];
}];
しかし、正直なところ、ほとんどすべてのユーザーがそうするのを知らないか、そうするのを忘れていると確信しています。API は使いやすいだけでなく、誤用されにくいものであるべきですが、これは明らかにその原則に違反しています。API のユーザーは間違いなく誤用します。
私を悩ませているのは、「自己」を保持する必要がないことを知っていることです。現在の実装では、保持しなくても機能します。だから私は、彼が自分自身を保持する必要がないことをブロックに伝えたい - 私、ライブラリは、ユーザーがそれについて考える必要がないように、ブロックにそれを伝える必要がある.
私の研究では、そうする方法は見つかりませんでした。そして、そのブロックの制限に合わせてアーキテクチャを変更する方法が思いつきません。
私がそれについて何ができるか考えている人はいますか?
まだ読んでいない場合でも、ここまで読んでくれてありがとう -- 冗長な質問だったことは承知しています ;-)