wxactly
wxactly

Reputation: 2470

Within a block, what is the practical difference between a __block variable and a static variable?

I want to reuse an object reference across multiple calls of a single block, and I'm curious: What is the practical difference between the following two approaches?

Using a __block variable:

__block Widget *widget = [self buildNewWidget];

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

        gadget.widget = widget;
    }];
}

Using a static variable:

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

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

        gadget.widget = widget;
    }];
}

Obviously these two chunks of code differ from a semantic perspective, but (practically speaking) I believe they do the same basic work. My guess is that there is a difference from a memory management perspective, a performance perspective, or something else. Any insights that illustrate these differences (or explain why they are not different) would be helpful.

Upvotes: 5

Views: 548

Answers (4)

jtbandes
jtbandes

Reputation: 118691

An example is worth a thousand words:

(And yes, this is a very simplistic example, but it is mostly equivalent to what you were doing...)

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>

Upvotes: 2

tc.
tc.

Reputation: 33592

For the purposes of this answer, assume that both examples are wrapped in -(void)useGadgetsOnWidgets { ... }.

Assuming ARC, that your app is single-threaded and the code is non-reentrant (i.e. useGadgetsOnWidgets does not call itself), and that the block is not used after the method returns, there is one main difference:

With a static variable, widget sticks around forever. This means widgets get reused across calls to -useGadgetsOnWidgets (which can be good and bad), but also means that the Widget gets retained forever. You can change this by pulling the widget out of the loop/Block (I've also inited it at the start to more closely resemble the __block version:

-(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;
}

There is a third variant which is somewhat thread-safe and assumes that the block is not used after the method returns:

-(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 ;
    }];
  }
}

This seems slightly nicer than using a static variable (which is effectively just a global variable), but it's still pretty icky. Neither are techniques I'd want to teach to a novice programmer (but then again, neither is any sort of multithreading).

EDIT: For the problem you describe, a better solution than any of these is to just cache the widget in an ivar/property on 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];
    }];
  }
}

Upvotes: 1

Ramy Al Zuhouri
Ramy Al Zuhouri

Reputation: 21966

__block makes the variable available inside the block, like if it's global. If you use it inside the block, instead of being copied it will be referenced and since it's like global it will be still alive.But the next time you call that block of code, another variable will be pushed in the stack.

static makes the variable visible only in the scope, and it survives the entire execution of the program. But if you call that block of code another time, the variable will be the same.

Upvotes: -1

rmaddy
rmaddy

Reputation: 318824

As written, these two code fragments work differently and they have different end results.

The second set of code is a failure waiting to happen. It will fail if this code is run on two different threads at the same time due to the use of the static variable. This code is also going to fail because you never initialize the static variable. The first time the if statement is reached, the app could crash.

Since each iteration of the loop appears to depend on the current value of widget, you need have a local variable that is initialized before the loop. Since this variable needs to be modified inside a block, you need to make the variable a __block variable. This means your first set of code is the proper code.

Upvotes: 1

Related Questions