I don't know what patterns of block I should avoid for the literal scope in objective-c
Asked Answered
W

2

11

It is said in apple's documents: A block literal (that is, ^{ ... }) is the address of a stack-local data structure that represents the block. The scope of the stack-local data structure is therefore the enclosing compound statement, so you should avoid the patterns shown in the following examples:

void dontDoThis() {

    void (^blockArray[3])(void);  // an array of 3 block references

    for (int i = 0; i < 3; ++i) {

        blockArray[i] = ^{ printf("hello, %d\n", i); };

        // WRONG: The block literal scope is the "for" loop.
    }

    //for example I invoke the block here
    blockArray[1]();
  }


void dontDoThisEither() {

    void (^block)(void);

    int i = random():

    if (i > 1000) {

        block = ^{ printf("got i at: %d\n", i); };

        // WRONG: The block literal scope is the "then" clause.

    }

    // ...

  }

I don't know what patterns I should avoid. It seems like that I could invoke the block at which has the same literal scope as the block definition, for example behind the "if" or "for" statement. Could you please help me to explain it?

Here it is the link https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Blocks/Articles/bxUsing.html#//apple_ref/doc/uid/TP40007502-CH5-SW1

Wimble answered 27/11, 2012 at 23:56 Comment(0)
I
9

I think an analogy to pointers is as following.

void foo() {
  int *block = NULL;
  {
    int a;
    block = &a;
  }
  // `block`, even though defined here, points to
  // an invalid memory address.
}

Generally, the block literal itself only exists in the block it is defined in, so when leaving that block, the literal disappears (like the variable a did in the example above), and you're left with a dangling pointer.

For this reason, blocks are usually copied into the heap for future use. Non-ARC code uses block_copy and friends. Copying into the heap also captures all relevant variables your block uses (which might create retain-cycles).

In practice, all of this is quite shunned away by the use of ARC, properties and classes. You define a copy property in your class, and then just assign blocks to it. If you let the compiler generate the getter/setter, your block literal will automatically be copied into the heap.

@interface Bla : NSObject
@property (nonatomic, copy) void (^blockProperty)(int i);
@endf

...

Bla *bla = [[Bla alloc] init];
{
  bla.blockProperty = ^(int i) { printf("%d", i); };
}
// bla.blockProperty now points to a heap copy of the block literal from above,
// so it's not dangling.
Incoordination answered 28/11, 2012 at 0:7 Comment(4)
Thank you. And do you means that the block should be declared as a copied property, instead of a stack variable in method?Wimble
or I can declare a block as a global variable? Actually, it should be stored in local-stack as well, seen in everywhere.Wimble
Got it! Thank you very much. The block compound statement likes a integer value substantially, as the address of a stack-local data structure.Wimble
@JerryRay: a local block variable is perfectly fine. But if you want to store a block literal that's in an inner scope and use it outside, make sure to store a copy of it (and the copy needs to be done in the scope where the block is still valid). e.g. blockArray[i] = [[^{ printf("hello, %d\n", i); } copy] autorelease];Several
L
0

I was reading apple's documentation on blocks and did a little more research on this part. It looks to me with ARC the second example code is totally fine. I didn't try the first example. The general idea in the accepted answer is correct. However, with ARC, when you assign the literal block (NSStackBlock) to a local variable, the block is copied to heap, and if you inspect the block, you'll see it's indeed a NSMallocBlock. I also referenced this blog on this topic https://www.cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html

Literacy answered 24/1, 2017 at 3:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.