6

ARCリリースノートへの移行から

強力な参照サイクルを回避するために生涯修飾子を使用する

ライフタイム修飾子を使用して、強力な参照サイクルを回避できます。たとえば、通常、親子階層に配置されたオブジェクトのグラフがあり、親が子を参照する必要がある場合、またはその逆の場合は、親と子の関係を強くし、子と親の関係を弱くします。 。他の状況は、特にブロックオブジェクトが関係する場合は、より微妙な場合があります。

手動参照カウントモードで__block id x;は、を保持しないという効果がありxます。ARCモードでは、__block id x;デフォルトで保持されますx(他のすべての値と同様)。ARCでの手動参照カウントモードの動作を取得するには、を使用できます__unsafe_unretained __block id x;。ただし、名前__unsafe_unretainedが示すように、保持されていない変数を持つことは危険であり(ぶら下がる可能性があるため)、したがって推奨されません。2つのより良いオプションは、使用するか__weak(iOS4またはOSX v10.6をサポートする必要がない場合)、または保持サイクルを中断するように__block 値を設定することです。nil

さて、__block変数の違いは何ですか?

なぜnilここに設定するのですか?__block変数は2回保持されますか?誰がすべての参照を保持しますか?ブロック?ヒープ?スタック?スレッド?なに?

次のコードフラグメントは、手動参照カウントで使用されることがあるパターンを使用してこの問題を示しています。

MyViewController *myController = [[MyViewController alloc] init…];

// ...

myController.completionHandler =  ^(NSInteger result) {
   [myController dismissViewControllerAnimated:YES completion:nil];
};

[self presentViewController:myController animated:YES completion:^{
   [myController release];
}];

説明したように、代わりに、修飾子を使用して、完了ハンドラーで__blockmyController変数をに設定できます。nil

MyViewController * __block myController = [[MyViewController alloc] init…]; //Why use __block. my controller is not changed at all

// ...

myController.completionHandler =  ^(NSInteger result) {
    [myController dismissViewControllerAnimated:YES completion:nil];

    myController = nil; //Why set to nil here? Is __block variable retained twice? Who hold all the reference? The block? The heap? The stack? The thread? The what?
};

また、なぜコンパイラによってmyController設定されないのですか。nilなぜそうしなければならないのですか?コンパイラは、myControllerが再び使用されなくなる時期、つまりブロックが期限切れになる時期をある程度知っているようです。

4

2 に答える 2

14

この形式のコードがある場合:

object.block = ^{
    // reference object from inside the block
    [object someMethodOrProperty];
};

objectあなたがそれに与えたブロックを保持またはコピーします。objectただし、ブロック内から強く参照されるため、ブロック自体も保持されます。これは保持サイクルです。ブロックの実行が終了した後でも、参照サイクルは存在し、オブジェクトもブロックの割り当ても解除できません。ブロックは複数回呼び出すことができるため、1回の実行が終了した後、参照するすべての変数を忘れることはできません。

このサイクルを中断するobjectには、__block変数として定義できます。これにより、ブロック内から値を変更できます。たとえばnil、サイクルを中断するように変更します。

__block id object = ...;
object.block = ^{
    // reference object from inside the block
    [object someMethodOrProperty];

    object = nil;
    // At this point, the block no longer retains object, so the cycle is broken
};

ブロックの最後にを割り当てるobjectnil、ブロックは保持されなくなりobject、保持サイクルが中断されます。これにより、両方のオブジェクトの割り当てを解除できます。

この具体的な例の1つは、with NSOperation'scompletionBlockプロパティです。を使用しcompletionBlockて操作の結果にアクセスする場合は、作成された保持サイクルを中断する必要があります。

__block NSOperation *op = [self operationForProcessingSomeData];
op.completionBlock = ^{
    // since we strongly reference op here, a retain cycle is created
    [self operationFinishedWithData:op.processedData];

    // break the retain cycle!
    op = nil;
}

ドキュメントで説明されているように、これらの保持サイクルを中断するために使用できる他の多くの手法があります。たとえば、非ARCコードでは、ARCコードとは異なる手法を使用する必要があります。

于 2012-06-12T14:05:36.533 に答える
0

私はこの解決策を好む

typeof(self) __weak weakSelf = self;
self.rotationBlock = ^{
    typeof (weakSelf) __strong self = weakSelf;

    [self yourCodeThatReferenceSelf];
};

何が起こるかというと、ブロックは参照として自己をキャプチャし、保持サイクルはありません。ブロック内のselfは、コードが実行される前に__strong self=weakSelfとして再定義されます。これにより、ブロックの実行中に自己が解放されるのを防ぎます。

于 2014-11-11T13:36:26.597 に答える