Reputation: 2470
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
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
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
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
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