0

ご存じのとおり、ARC では__block、ブロックで使用されるオブジェクト ポインター型の変数は、ブロックによって保持されます。したがって、次の単純化された例を見てください。

__block id foo = getObject();
void (^aBlock)() = ^ {
    NSLog(@"%@", foo);
    foo = getObject();
}
runBlockAsynchronouslyMultipleTimes(aBlock);

が指すオブジェクトfooはブロックによって保持されるため、ブロックが (非同期で) 実行されても、オブジェクトは引き続き有効であり、出力できます。ブロック内で割り当てを行うと、ARC は他の強参照と同様にそれを管理します (古い値は解放され、新しい値は保持されます)。(割り当てにより、最初に使用することが強制され__blockます。) そして、ブロックが不要になると、ARC はそのfoo時点で が指す保持オブジェクトを何らかの方法で解放します (リークされません)。

さて、MRC の下で同じことをしたいとしましょう (なぜ重要でないのか。これは言語に関する問題です)。ご存知のよう__blockに、ブロックで使用されるオブジェクト ポインター型の変数は、MRC のブロックによって保持されません。どちらでも構いません。自分たちで管理します (これは MRC です)。したがって、試行は次のようになります。

__block id foo = [getObject() retain];
void (^aBlock)() = ^ {
    NSLog(@"%@", foo);
    [foo release];
    foo = [getObject() retain];
}
runBlockAsynchronouslyMultipleTimes(aBlock);
// where to release foo?

そのほとんどは簡単です。オブジェクトは、最初は手動で保持されます。ブロック内では、ポインターを再割り当てするときに、必要に応じて新しい値を解放して保持します。

しかし、問題は次のとおりです。ブロックが不要になったときにオブジェクトを解放するにはどうすればよいでしょうか。手動でメモリを管理するため、理想的には、ブロックの割り当てが解除されたときにオブジェクトを手動で解放する必要があります。しかし、これを行う簡単な方法はないようです。

おそらく1つの方法を考えることができます.連想参照を使用してオブジェクトをブロックに結び付けます。ただし、ブロック内で連想参照を再割り当てするには、ブロック自体への参照が必要になるため、ブロック変数も必要であり__block、変数を設定する前にブロックをコピーする必要があります。これはすべて非常に醜いです。または、ブロックによって保持される変更可能なコンテナー オブジェクト内にオブジェクトを配置します。しかし、それも醜いです。

4

1 に答える 1

0

変更可能なコンテナーは、可能な限りクリーンです。単一のobjectプロパティを持つ単純なラッパーを作成して少しクリーンアップすると、プロパティ アクセサーからメモリ管理を取得できます。

よりきれいに見えますが、実際にはちょっと厄介なアプローチは、ポインターを取り、割り当てが解除されたときにそのポインターを解放する不変のラッパーを持つことです。

@interface ObjectReleaser : NSObject {
    id *objectPointer;
}
- (id)setObjectPointer:(id *)pointer;
- (void)captureMe;
@end

@implementation ObjectReleaser
- (void)setObjectPointer:(id *)pointer {
    if(!objectPointer && pointer) {
        objectPointer = pointer;
        [*objectPointer retain];
    }
}
- (void)dealloc {
    if(objectPointer) [*objectPointer release];
    [super dealloc];
}
- (void)captureMe {} // Blocks can call this to capture the object
@end

ではないため、ブロックはこのオブジェクトをキャッチして保持します__block。適切なすべての および__blockを使用して、通常どおりオブジェクトを変更します。次に、ブロックの割り当てが解除されると、リリーサーが解放されます。リリーサーは割り当てが解除され、ポインターが現在指しているすべてのものを解放します。retainrelease

__block id foo = getObject();
ObjectReleaser *releaser = [[ObjectReleaser alloc] init];
void (^aBlock)() = ^ {
    [releaser captureMe];
    NSLog(@"%@", foo);
    [foo release];
    foo = [getObject() retain];
}
aBlock = [aBlock copy];
[releaser setObjectPointer:&foo];

fooリリーサーが保持するため、ブロックのためだけに保持する必要はないことに注意してください。ブロックをコピーすると のポインタが変更されるため、ブロックをコピーしたにリリーサのポインタを設定する必要があります。これが、関数が戻った後にスタック変数のポインターを保存しても安全な理由でもあります。変数は実際にはスタック上にありません。foo

于 2012-10-03T20:11:23.763 に答える