3

I want to make a property that allows a block to update its contents. For a typical variable (i.e. not auto-synthesized, etc), I would prefix the declaration with __block.

Is there a way to make a property that is able to be updated by a block?

Edit: Some answers say just to use the properties setter method. What I actually want to do is add objects to a mutable array, so I don't really want to create a new mutable array and set it back to the property using setProperty: or .property = each time. Is it possible to modify the property without all of these memory allocations?

paulrehkugler
  • 3,241
  • 24
  • 45
  • As is noted in several answers below, you're free to modify a mutable array inside of a block without declaring anything with `__block`. What actual problem are you encountering? – Rob Napier Sep 03 '13 at 16:05
  • 1
    @RobNapier The actual problem I'm encountering seems to be with my understanding of block handling. See my comments on the accepted answer. – paulrehkugler Sep 03 '13 at 16:14

4 Answers4

3

This isn't the right way to think about it. You want to modify an object, which has a property. So you capture the object in the block, and call its accessors (setFoo:) to modify the property.


EDIT: From the various edits, you may be confusing how ObjC objects work and how C++ works. ObjC only uses pointers to objects, seldom copies them, and lacks the concept of a const object (there are immutable objects, but it's just because they don't have mutators; you can't const a mutable object like you can in C++).

__block just means "this variable (not the object pointed to by it; this actual variable) should be passed by reference into the block rather than by value)." So when I say:

__block id foo;

this means that the foo pointer itself can be changed. It has nothing at all to do with whether the object pointed to by foo can be mutated. This makes no sense for a global or an ivar. ivars are implicitly struct fields. When you say _ivar inside a block, the compiler implicitly converts this to self->_ivar and then captures self. It does not capture _ivar (since that's just an offset into the self struct). It's better to do the same using accessors, since it's much more explicit what you're doing, and you can use __weak that way.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • I don't understand your edits. If you want to add objects to a mutable array, just call `[[myObject someArray] addObject:foo]` (where `myObject` is whatever object you've captured in the block). What memory allocations are you talking about here? – Rob Napier Sep 03 '13 at 16:01
  • People often use _ _block_ unnecessarily for mutable arrays / dictionaries. You can just outside the block write NSMutableArray* myArray = [NSMutableArray array]; and use myArray inside the block. The block captures myArray - which is just a pointer to an instance of NSMutableArray. You can't modify myArray inside the block, but that's just the pointer. You _can_ modify the NSMutableArray object itself as much as you like, for example [myArray addObject:@"Hello"];. This adds an object to the array that myArray points to; it doesn't change myArray (the pointer). – gnasher729 Mar 24 '14 at 13:28
3

__block is used to declare local variables that a block might want to change.

Neither are properties local variables, nor is there a need to change a variable when all you want to do is add something to a mutable array. The variable does not change, it's the object's state behind the variable that changes.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
2

You use __block when you want to modify a variable within a block; in your case what you want to do is not modifying the variable(it will still point to the same NSMutableArray) but just sending a message to it(addObject:). You don't need __block for that.

e1985
  • 6,239
  • 1
  • 24
  • 39
1

There is no need to put anything into a @property declaration. since they can be accessed and modified by blocks without any trouble. According to the documentation:

Within the block object’s body of code, variables may be treated in five different ways.

You can reference three standard types of variable, just as you would from a function:

  1. Global variables, including static locals
  2. Global functions (which aren’t technically variables)
  3. Local variables and parameters from an enclosing scope

Blocks also support two other types of variable:

  1. At function level are __block variables. These are mutable within the block (and the enclosing scope) and are preserved if any referencing block is copied to the heap.
  2. const imports.

The following rules apply to variables used within a block:

  1. Global variables are accessible, including static variables that exist within the enclosing lexical scope.
  2. Parameters passed to the block are accessible (just like parameters to a function).
  3. Stack (non-static) variables local to the enclosing lexical scope are captured as const variables.
    Their values are taken at the point of the block expression within the program. In nested blocks, the value is captured from the nearest enclosing scope.
  4. Variables local to the enclosing lexical scope declared with the __block storage modifier are provided by reference and so are mutable.
    Any changes are reflected in the enclosing lexical scope, including any other blocks defined within the same enclosing lexical scope.
  5. Local variables declared within the lexical scope of the block, which behave exactly like local variables in a function.
    Each invocation of the block provides a new copy of that variable. These variables can in turn be used as const or by-reference variables in blocks enclosed within the block.
Raúl Juárez
  • 2,129
  • 1
  • 19
  • 17
  • How will the compiler know to add __block to the ivar? This SO answer (http://stackoverflow.com/a/7081005/953105) makes it seem necessary. – paulrehkugler Sep 03 '13 at 15:49
  • Based on these, it's implied that the compiler, by default, copies function variables that are not __block defined, but does not copy global/class-accessible variables. For some reason, I thought that *everything* was copied. – paulrehkugler Sep 03 '13 at 16:12
  • 1
    You are correct; it only copies automatic variables that lack the `__block` storage modifier. – Rob Napier Sep 03 '13 at 16:14
  • 6
    Ivars fall under rule #5, because the block const-captures `self`, and not the ivar directly. Ivars are then looked up via `self->_ivarName`. So even though `self` is `const`, its members aren't, which is why you can reference ivars in a block without having to declare them as `__block`. – Dave DeLong Sep 03 '13 at 16:20
  • "they are global variables" what? – newacct Sep 04 '13 at 02:59