5

1つのブロックの複数の呼び出しでオブジェクト参照を再利用したいのですが、興味があります。次の2つのアプローチの実際的な違いは何ですか。

__block変数の使用:

__block Widget *widget = [self buildNewWidget];

for(Gadget *gadget in self.gadgets) {
    [self useGadget:gadget withCallback:^{
        if([widget isBroken]) {
            widget = [self buildNewWidget];
        }

        gadget.widget = widget;
    }];
}

static変数の使用:

for(Gadget *gadget in self.gadgets) {
    [self useGadget:gadget withCallback:^{
        static Widget *widget;

        if(!widget || [widget isBroken]) {
            widget = [self buildNewWidget];
        }

        gadget.widget = widget;
    }];
}

明らかに、これらの2つのコードのチャンクはセマンティックの観点からは異なりますが、(実際には)同じ基本的な作業を行っていると思います。私の推測では、メモリ管理の観点、パフォーマンスの観点、または他の何かとの違いがあります。これらの違いを説明する(または違いがない理由を説明する)洞察があれば役立ちます。

4

4 に答える 4

2

例は千の言葉の価値があります:

(はい、これは非常に単純な例ですが、ほとんどの場合、あなたが行っていたことと同等です...)

for (int i = 0; i < 3; i++)
{
    // Your example encompasses this scope,
    // not taking into account that we may execute this code multiple times:

    // Call the block
    (^{
        // Every instance/execution of this block will have the same object.
        static Obj *o;

        // Initialize static object
        static dispatch_once_t once;
        dispatch_once(&once, ^{
            o = [Obj new];
        });

        NSLog(@"Object is: %@", o);
    })();
}
// Output:
//   Object is: <Obj: 0x100109fd0>
//   Object is: <Obj: 0x100109fd0>
//   Object is: <Obj: 0x100109fd0>

for (int i = 0; i < 3; i++)
{
    __block Obj *o = [Obj new];

    // Call the block
    (^{
        // This block uses the object from its enclosing scope, which may be different.
        NSLog(@"Object is: %@", o);
    })();
}
// Output:
//   Object is: <Obj: 0x105100420>
//   Object is: <Obj: 0x1003004f0>
//   Object is: <Obj: 0x105300000>
于 2013-01-27T01:46:01.770 に答える
1

記述されているように、これら2つのコードフラグメントは動作が異なり、最終結果も異なります。

コードの2番目のセットは、発生するのを待っている障害です。静的変数を使用しているため、このコードを2つの異なるスレッドで同時に実行すると、失敗します。静的変数を初期化しないため、このコードも失敗します。初めてifステートメントに到達すると、アプリがクラッシュする可能性があります。

ループの各反復はの現在の値に依存しているように見えるため、widgetループの前に初期化されるローカル変数が必要です。この変数はブロック内で変更する必要があるため、変数を変数にする必要があり__blockます。これは、最初のコードセットが適切なコードであることを意味します。

于 2013-01-26T23:15:32.423 に答える
1

この回答の目的のために、両方の例がでラップされていると仮定し-(void)useGadgetsOnWidgets { ... }ます。

ARCを想定すると、アプリはシングルスレッドであり、コードは再入可能ではなく(つまりuseGadgetsOnWidgets、それ自体を呼び出さない)、メソッドが戻った後にブロックが使用されないという場合、主な違いが1つあります。

static変数を使用すると、widget永遠に固執します。これは、ウィジェットがへの呼び出し全体で再利用されることを意味します-useGadgetsOnWidgets(これは良いことも悪いこともあります)が、ウィジェットが永久に保持されることも意味します。ウィジェットをループ/ブロックから引き出すことでこれを変更できます(バージョンにもっと似せるために、最初にウィジェットを初期化しました__block

-(void)useGadgetsOnWidgets {
  static Widget *widget;
  widget = [self buildNewWidget];
  for(Gadget *gadget in self.gadgets) {
    [self useGadget:gadget withCallback:^{
      if([widget isBroken]) {
        widget = [self buildNewWidget];
      }
      gadget.widget = widget;
    }];
  }
  widget = nil;
}

ある程度スレッドセーフであり、メソッドが戻った後にブロックが使用されないと想定する3番目のバリアントがあります。

-(void)useGadgetsOnWidgets {
  Widget *widget = [self buildNewWidget];
  Widget ** pWidget = &widget;
  for(Gadget *gadget in self.gadgets) {
    [self useGadget:gadget withCallback:^{
      if([*pWidget  isBroken]) {
        *pWidget = [self buildNewWidget];
      }
      gadget.widget = *pWidget ;
    }];
  }
}

これは、変数(事実上単なるグローバル変数)を使用するよりも少し良いように見えstaticますが、それでもかなり厄介です。どちらも私が初心者プログラマーに教えたいテクニックではありません(しかし、繰り返しになりますが、どちらもマルチスレッドではありません)。

編集:あなたが説明する問題については、これらのどれよりも良い解決策は、ウィジェットをivar /propertyにキャッシュすることですself

-(Widget*)workingWidget {
  // Assuming _cachedWidget is an ivar
  if ([_cachedWidget isBroken]) {
    _cachedWidget = [self buildWidget];
  }
  return _cachedWidget;
}

-(void)useGadgetsOnWidgets {
  for(Gadget *gadget in self.gadgets) {
    [self useGadget:gadget withCallback:^{
      gadget.widget = [self workingWidget];
    }];
  }
}
于 2013-01-27T02:21:59.867 に答える
-1

__blockは、グローバルであるかのように、ブロック内で変数を使用できるようにします。ブロック内で使用すると、コピーされる代わりに参照され、グローバルのように機能しますが、次にそのコードブロックを呼び出すと、別の変数がスタックにプッシュされます。

staticを使用すると、変数はスコープ内でのみ表示され、プログラムの実行全体で存続します。ただし、そのコードブロックをもう一度呼び出すと、変数は同じになります。

于 2013-01-26T23:20:26.310 に答える