Objective-C blocks

守給你的承諾、 提交于 2019-12-12 09:19:06

问题


Trying to understand how blocks working in objective-c. Got next question while reading apple's docs (link)

Here is an example how we should no use blocks:

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.
    }
}

But how we could get 3 different blocks that will print "hello, 0", "hello, 1" and "hello, 2"? I tried many different ways but every time I got "hello, 2" three times.


回答1:


A block starts out life on the stack and, thus, a block's lifespan is only as long as the scope it is declared in.

The body of a for() loop -- the body of the loop in the {}s -- is a scope in and of itself. Thus, your code is putting a reference to something on the stack [the block] into a variable in the surrounding scope [the language array].

You need to copy the block to the heap to have it survive:

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); } copy];
    }
}

If not using ARC, you would also need to -release the copied blocks at some point.

You might find this weblog post handy (I wrote it shortly after Blocks were made public). This one goes into a few tips, tricks, and gotchas.


Wait -- yeah -- you're correct. There is magic going on in the ARC compiler that is causing the blocks to seemingly be on the heap magically. However, I can't find anything in the LLVM documentation that explicitly documents this behavior. If you turn off ARC, you'll see the output be something like 2,2,2 instead of 0,1,2.

This is somewhat new behavior. I wouldn't rely on this behavior until someone can find the explicit note in the compiler that defines exactly how this is supported.


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

    for (int i = 0; i < 3; ++i) {
        void (^block)(void) = ^{ printf("hello, %d\n", i); };
        NSLog(@"%p", block);
        blockArray[i] = block;
        NSLog(@"%p", blockArray[i]);
    }

    for (int i = 0; i < 3; ++i) blockArray[i]();
}

Outputs:

2012-12-24 16:15:36.752 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.755 jkdfjkfdjkdfjk[70708:303] 0x100108160
2012-12-24 16:15:36.758 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.759 jkdfjkfdjkdfjk[70708:303] 0x100108000
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x100102e70
hello, 0
hello, 1
hello, 2

Thus, the block is created on the stack and copied to the heap automatically on the assignment outside of the scope of the for() loop.

A similar test also reveals that the block will be copied when passed as an argument to NSArray's addObject:.




回答2:


If you really wanted to get this to work you could use an NSMutableArray instead of a C array. NSMutableArray *blocks = [NSMutableArray array];

for (int i = 0; i <= 3; i++) {
  blocks[i] = ^{ printf("hello %d\n", i); };
}

By adding them to an NSMutableArray they will be copied off of the stack and onto the heap allowing them to outlive the scope of the for loop.

As @bbum points out the above does not work, I took the idea that blocks just work with ARC too far.

You would need to actively copy the blocks for them to work... so the following should work

NSMutableArray *blocks = [NSMutableArray array];

for (int i = 0; i <= 3; i++) {
  blocks[i] = [^{ printf("hello %d\n", i); } copy];
}


来源:https://stackoverflow.com/questions/14026083/objective-c-blocks

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!