5

CAAnimationアニメーションの最後に処理を提供するために完了ブロック ( CAAnimationBlocksを使用) を使用しており、その完了ブロックの一部がアニメーションを変更しCALayerます。これlayerは、指定子で宣言されていなくても機能し__blockますが、オブジェクト ポインターは一定のままですが、実際にはオブジェクトを読み取り/書き込みとして扱っています。

私を悩ませているApple ガイドの 1 つの側面は次のとおりです。

__block 変数は、変数のレキシカル スコープと、変数のレキシカル スコープ内で宣言または作成されたすべてのブロックおよびブロック コピーとの間で共有されるストレージに存在します。

がコレクション イテレータであることを考えると、指定子layerを使用すると実際に壊れてしまうように見え__blockます。

問題のコードは次のとおりです。

for (CALayer *layer in _myLayers)   // _myLayers is an ivar of the containing object
{
    CAAnimationGroup *group = ...;
    ...
    group.completion = ^(BOOL finished)
    {
        [CATransaction begin];
        [CATransaction setValue:(id)kCFBooleanTrue
                         forKey:kCATransactionDisableActions];
        layer.position = [self _findPosition];
        [CATransaction commit];

        [layer removeAnimationForKey:@"teleportEffect"];
    };

    [layer addAnimation:group forKey:@"teleportEffect"];
}

私の実際の質問は次のとおりです。私はこれを正しく行っていますか(私のクモの感覚はチクチクしています)。

EDITまた、アプリが MRR を使用していることも追加する必要がありますが、レイヤーが本質的に静的であることを考えると、保持/解放に問題はありません(それらの有効期間は含まれているものですNSView)。また、ガイドの「避けるべきパターン」セクションで「すべきではない」と書かれていることを正確に行っているように見えますが、その理由は (私には) 明らかではありません。

4

2 に答える 2

4

__block変数はブロック(およびそれを囲むスコープ)内で変更可能であり、参照ブロックがヒープにコピーされた場合に保持されます。

ブロック内のオブジェクトの値を変更しているため、ブロック変数は必要ないと思います。これは、インスタンス変数のように見える配列layerに属しているため_myLayers、オブジェクトが以前に解放されることは困難です。実行された各ブロック...ただし__block、オブジェクトを保持するためにストレージタイプ修飾子を追加できますが、 ARCを使用している場合は、ブロックがコピーされて後で解放されるときに、オブジェクト変数が自動的に保持および解放されます。

于 2012-09-16T09:03:52.220 に答える
1

編集:

あなたが言及したアンチパターンに関する懸念については、両方のアンチパターンの例で重要な点は、変数宣言とそれに割り当てられた「ブロックリテラル」のスコープが異なることだと思います。forそこで提示されたケースを取り上げます。

void dontDoThis() {
  void (^blockArray[3])(void);  // an array of 3 block references

  for (int i = 0; i < 3; ++i) {
    blockArray[i] = ^{ printf("hello, %d\n", i); };
    // WRONG: The block literal scope is the "for" loop
  }
}
  1. blockArrayメソッド本体全体に表示されます。

  2. forループでは、ブロックを作成します。ブロックはオブジェクト(メモリ内の一部のストレージ)であり、アドレスを持っています。オブジェクトとしてのブロックには「スタックローカル データ構造」があります (上記の参照から)。つまり、メソッドに入るとスタックに割り当てられます。

  3. 「ブロックリテラル」がforループに対してローカルな変数として扱われるという事実は、そのストレージが連続する反復ごとに再利用できることを意味します。

  4. blockArrayブロック アドレスは要素に割り当てられます。

  5. forループを終了すると、コンパイラがスコープblockArray内で作成されたデータ構造をスタックするために何をするかに応じて、おそらくもう存在しないか、各ステップで上書きされたブロックのアドレスが含まれます。for

ローカル変数もforスコープ内にあり、その外側には表示されないため、ケースは異なります。

アンチパターンとして提示されるケースは、次のようなものです。

 {
 int array[3];

 for (int i = 0; i < 3; ++i) {
    int a = i;
    array[i] = &a;
    // WRONG: The block literal scope is the "for" loop
 }

おそらく、スコープa内の変数はfor一度だけスタックに割り当てられ、ループの各反復で再利用されます。原則として、a(1 つのコピー) はまだループの外側に存在します (実際には、C 標準を検査する必要があるかどうかはわかりません) が、そのコードの意味が実際には賢明でないことは明らかです。

古い答え:

__block 変数は、変数のレキシカル スコープと、変数のレキシカル スコープ内で宣言または作成されたすべてのブロックおよびブロック コピーとの間で共有されるストレージに存在します。

これは次のように理解するとよいと思います:__block変数のレキシカル スコープとすべてのブロック (上記の定義による) は、その変数に対して同じストレージを共有します。したがって、1 つのブロック (または元のレキシカル スコープ) が変数 (ここでは、オブジェクトを指す変数を意味します) を変更すると、その変更は他のすべてに表示されます。

これを考えると、変数をそのまま宣言する効果の 1 つ__block、非 ARC の場合、それが指すオブジェクトは、それが渡される各ブロックによって自動的に保持されない__blockということです (ARC では、保持は変数に対しても行われます) 。 .

ARC を使用する場合と使用しない場合の両方で__block、変数の値を変更し、すべてのブロックで新しい値を使用する場合は、指定子を使用する必要があります。ivarを初期化するためのブロックがあるとします。_myLayersこの場合、_myLayers変数を変数としてブロックに渡す必要があります。これにより、__block変数を (コピーではなく) 変更できるようになります。

あなたの場合、ARCを使用していないlayer場合、ブロックが実行されたときに指されたオブジェクトがまだそこにあるかどうかに依存します。layerから来ているので、これは、ブロックが実行されたときに所有しているオブジェクトがまだそこにある_myLayersかどうかに変換されます。_myLayers私たちが話しているブロックはそのレイヤーのアニメーションの完了ブロックであるため、これに対する答えは通常はいです。(たとえば、それがネットワーク リクエストの完了ブロックである場合は、別のものになります)。

お役に立てれば。

于 2012-09-16T09:16:36.920 に答える