How can I refer to the current block object within my block function? - objective-c

I know that a block descriptor is passed on the stack to a block function when it is invoked. Is there a variable name I can use to refer to this in my code (like self or _cmd for methods)
(^{
// how can I access the block descriptor here?
})();
edit
I actually want the block object, not the block descriptor...

In short, you can't. At least not directly (there is nothing akin to self within a block -- we thought long and hard about that, but couldn't come up with something both elegant nor enough need for it in light of the following pattern to justify adding such syntax).
If you want to refer to the block, you need to do something like:
__block void(^strawberryFields)();
strawberryFields = ^{ strawberryFields(); };
strawberryFields();
Note that the above will run forever. Note also that you might want to copy that block upon assignment if you plan on using the block later.
Consider:
NSMutableArray *array = [NSMutableArray array];
int i;
for(i = 0; i<5; i++) {
[array addObject:[^{ return i*i; } copy]];
}
You'll end up with an array with 5 blocks, each capturing a different value of i.

It may help to create a method to initialize each block for you. Here's a quick test that demonstrates each block has its own variable:
-(void (^)(void))intAddingBlock:(NSString *)name {
__block int intForThisBlock = 0;
return ^{
NSLog(#"%# before: %d", name, intForThisBlock);
intForThisBlock += 5;
NSLog(#"%# after: %d", name, intForThisBlock);
};
}
-(void)testTheBlock {
void(^block1)(void) = [self intAddingBlock:#"block 1"];
void(^block2)(void) = [self intAddingBlock:#"block 2"];
block1();
block1();
block2();
block1();
block2();
}
Output:
block 1 before: 0
block 1 after: 5
block 1 before: 5
block 1 after: 10
block 2 before: 0
block 2 after: 5
block 1 before: 10
block 1 after: 15
block 2 before: 5
block 2 after: 10

Related

Objective C: Can a block variable be used only once?

Suppose I have a need for multiple block "calls", for instance a loop in which the block is passed to a another function in each iteration. Do I need to make a new block instance each time I call the function (like Example 1) or can I make one block instance that is called each time (like Example 2)?
//Example 1:
while(true){
void (^block)(NSString* test)=^(NSString* test){
//do something
};
[self callWithBlock: block];
}
//Example 2
void (^block)(NSString* test)=^(NSString* test){
//do something
};
while(true){
[self callWithBlock: block];
}
It compiles and runs fine the second way, but I suspect that any concurrency issues may not be immediately obvious.
You can call blocks as often as you want. But you need to be careful about the context that is captured by the block.
If you have any values that are captured by your block, keep in mind, that unless when specifying them as __block variables, they will be copied.
So for example, this code:
int anInteger = 42;
void (^testBlock)(void) = ^{
NSLog(#"Integer is: %i", anInteger);
};
anInteger = 84;
testBlock();
will print 42, not 84.
If you declare anInteger as __block int anInteger = 42, the storage will be shared and the code will print 84.
So, if you have code that is something like:
int foo = 42;
void (^block)(void) = ^{
NSLog(#"%i", foo);
}
while (true) {
block();
foo++;
}
the behavior will be different from
int foo = 42;
while (true) {
void (^block)(void) = ^{
NSLog(#"%i", foo);
}
block();
foo++;
}
This will also apply to pointers and NSObject variables, when you reassign the variable that holds the pointer or object.
To find out more, have a look at Working with Blocks from the Apple developer documentation.

Rules for variable capture by block in objective-C

What are the semantics of capturing a variable by a block in objective-C?
#import <Foundation/Foundation.h>
#include <stdio.h>
int main()
{
NSMutableArray *arr = [NSMutableArray array];
for (int i = 0; i < 100; ++i) {
int j = i;
[arr addObject:^(void) {printf("%d %d\n", i, j); }];
}
for (void (^blk)(void) in arr) {
blk();
}
}
I was expecing this to print something like:
100 0
100 1
...
100 99
Instead, it prints:
99 99
99 99
...
99 99
How is it even possible that it's interpreting j as equal to 99 ? j isn't even alive outside the for loop.
Because you're not using ARC! Without it, your block isn't being copied. You're just getting lucky and running the very last block every single time.
The reason you're seeing 99 99 many times is simply due to undefined behaviour.
Let's take the first for-loop:
for (int i = 0; i < 100; ++i) {
int j = i;
dispatch_block_t block = ^(void) {printf("%d %d\n", i, j); };
[arr addObject:block];
}
[I've pulled out the block for clarity.]
Inside this for-loop, the block is created. It is created on the stack and never moved to the heap because there is no copy of the block to do so.
Each time around the for-loop, it's extremely likely (well, certain really) that the same stack space is used for the block. And then the address of the block (which is on the stack) is added to arr. The same address each time. But it's a new implementation of the block each time.
Once out of the first for-loop, arr contains the same value 100 times. And that value points to the last created block, which is still on the stack. But it's pointing to a block on the stack that can no longer be accessed safely because it's out of scope.
In the case of this example however, the stack space occupied by the block by sheer luck (OK, simple code) hasn't been reused. So when you go and use the block, it "works".
The correct solution is to copy the block when it is added to the array. Either by calling copy or letting ARC do that for you. That way, the block is copied to the heap and you have a reference counted block that will live as long as needed by the array and the scope in which it is created.
If you want to learn more about how blocks work and understand this answer more deeply, then I suggest my explanations here:
http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-1/
http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-2/
http://www.galloway.me.uk/2013/05/a-look-inside-blocks-episode-3-block-copy/

Objective C blocks and variables

+ (NSArray *)getArrayOfBubblesWithTitles:(NSArray *)titles andMainBubble:(BubbleContainer *)mainB {
UIColor *c = mainB.colour;
Corner corner = [Styles getCornerForPoint:mainB.frame.origin];
NSMutableArray *blocks = [[NSMutableArray alloc] init];
NSUInteger count = titles.count;
CGSize size = mainB.frame.size;
//TODO: calculation blocks frame
for (int a = 0; a < titles.count; a++) {
PositionCalculationBlock x = ^{
return [SimpleSelectionView getPositionOfObjectAtIndex:a outOfBubbles:count size:size fromCorner:corner];
};
[blocks addObject:x];
}
NSMutableArray *m = [[NSMutableArray alloc] init];
for (int a = 0; a < titles.count; a++) {
[m addObject:[[BubbleContainer alloc] initSubtitleBubbleWithFrameCalculator:blocks[a] colour:c title:titles[a] andDelegate:NO]];
}
return m;
}
I am not sure about whether my blocks will work right regarding the use of variables. In apple docs it says Any changes are reflected in the enclosing lexical scope, including any other blocks defined within the same enclosing lexical scope. I'm not sure what this means but i think it means that where i use the variable a in the for statement, only the highest value of a will be used in every block instead of 0-count. Also does using those instance variables (count, size) avoid having a strong pointer to objects like mainB? This is hard to test. I am far away from being able to run my code, so if you have any knowledge about blocks could you criticise?
Thanks
First, realize that the line you quote:
Any changes are reflected in the enclosing lexical scope, including any other blocks defined within the same enclosing lexical scope.
applies specifically and only to block-mutable variables, i.e. variables that carry the __block modifier. You don't have any block-mutable variables in the code snippet you've shown us, so that line doesn't apply.
i think it means that where i use the variable a in the for statement, only the highest value of a will be used in every block instead of 0-count
Variables used in a block but defined in an enclosing scope are normally read-only -- the compiler will complain that the variable isn't assignable if try to make a change. To make changes, the variable needs to be marked as block-modifiable using the __block modifier. Changes made to a block-modifiable variable are reflected in the enclosing scope.
Here's an example:
{
__block int anInteger = 42;
void (^testBlock)(void) = ^{
NSLog(#"inside the block anInteger is: %i", anInteger);
anInteger = 96;
};
NSLog(#"before the assignment anInteger is: %i", anInteger);
anInteger = 84;
NSLog(#"before the block anInteger is: %i", anInteger);
testBlock();
NSLog(#"after the block anInteger is: %i", anInteger);
}
The output is:
before the assignment anInteger is: 42
before the block anInteger is: 84
inside the block anInteger is: 84
after the block anInteger is: 96

Objective-C blocks

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.
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:.
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];
}

Blocks, loops and local variables

Consider the following code fragment:
for(/* some condition */) {
int x = rand();
[array addObject:^(){
NSLog(#"%d", x);
}]
}
for(void (^block)() in array) {
block();
}
Now I would expect this code snippet to print out all values assigned to x in that for loop; however it seems that all blocks share the same 'x' variable (presumably the last one).
Any idea why this is so and how I could fix the code to have each block contain the variable 'x' as it was at the time the block was defined?
The documentation specifically says not to do this. The reason is that blocks are allocated on the stack, which means they can go out of scope. For the same reason you can't access the variable x outside of the first for loop, you also shouldn't use that block. x has gone out of scope, along with the block itself, and could contain any value.
To get around this, you can take a copy of the block like so:
for(/* some condition */) {
int x = rand();
void(^logBlock)() = ^() { NSLog(#"%d", x); }
[array addObject:[[logBlock copy] autorelease]];
}
This moves the block onto the heap, and should fix your problem.