When and when not to use __block in Objective-C?

11,102

Solution 1

The question is really phrased in the wrong way. It's not "when do I need __block?", it's "what does __block do?". Once you understand what it does, you can tell when you need it.

Normally, when a block captures a variable (capturing occurs when a block references a variable outside itself), it creates a copy of the variable (note that in the case of objects it creates a copy of the pointer not the object itself), and if it's an object, retains it.

This means that with the normal behavior, you can't use a block to change a value outside the block. This code is invalid, for example:

int x = 5;
void(^block)() = ^{ x = 10; };

The __block qualifier makes two changes: most importantly, it tells the compiler that the block should capture it directly, rather than make a copy. That means you can update the value of variables outside the block. Less importantly, but still very relevantly, when not using ARC, it tells the compiler not to retain captured objects.

Solution 2

A code block has access to any variables that are within the scope that the block was declared in. However, any variable/object declared outside of the block's scope is immutable within the block. You can read it, but not change it. Setting the __block flag in the object's declaration allows it to be changed within the block's scope.

EDIT: Here's an example:

NSString *myString = @"hello";
dispatch_sync(dispatch_get_main_queue(), ^{
    myString = @"hello world";
});

This doesn't work, and you'll get an error message.

__block NSString *myString = @"hello";
dispatch_sync(dispatch_get_main_queue(), ^{
    myString = @"hello world";
});

Problem solved!

Share:
11,102
Josh Sklar
Author by

Josh Sklar

Updated on July 14, 2022

Comments

  • Josh Sklar
    Josh Sklar almost 2 years

    When using blocks, why do you need __block for some variables, and for other variables, such as function parameters, you do not?

  • Catfish_Man
    Catfish_Man about 11 years
    Actually this is still probably incorrect, but for a different reason. Because you're using dispatch_async, the enclosing scope is likely to no longer exist by the time myString is written to.
  • Chris C
    Chris C about 11 years
    Edited it to fix that, sort of. Without it being declared at the interface level theres not really a guarantee the original variable will still be in scope anyway, right?
  • Catfish_Man
    Catfish_Man about 11 years
    Not quite sure what you mean by 'the interface level', but the example looks fine now. The "use a serial queue as a lock to fetch something into a local" pattern is pretty common.
  • Gajendra Rawat
    Gajendra Rawat about 10 years
    nice explanation with example :)
  • Iulian Onofrei
    Iulian Onofrei over 8 years
    @Catfish_Man, I think he wanted to say "without it being declared as a property in the respective class, there's not ..."
  • Pedro Trujillo
    Pedro Trujillo about 4 years
    Excellent explanation.