In the Apple block documentation is an example of code not to write:
void dontDoThisEither() {
void (^block) (void);
int i = random();
if (i > 1000) {
block = ^{printf("got i at: %d\n", i); };
}
// ...
}
The comments for the code say the block literal scope is the "then" clause.
I don't understand what they mean by that, there is no then clause, which is presumably why they have put it in quotes. But why have they put it in quotes and what is the relationship to the block's scope?
Think of an if statement as:
if this then that else this other thing
The {... block = ...} is in the then that part of the if statement. That is, it is *a sub-scope of the scope of the dontDoThisEither() function.
Because blocks are created on the stack and are only valid within the scope of their declaration, that means that the block assignment in that example is only valid within the then that scope of the if statement.
I.e. Consider:
void dontDoThisEither() {
void (^block) (void);
int i = random();
if (i > 1000) {
block = ^{printf("got i at: %d\n", i); };
} else {
block = ^{printf("your number is weak and small. ignored.\n");};
}
block();
}
At the time block(); is executed the block that it is pointing to is in a scope that is no longer valid and the behavior will be undefined (and likely crashy in a real world example).
Related
I found an block example in the book "Effective Objective-C 2.0"
void (^block)();
if (/* some condition */) {
block = ^ {
NSLog(#"Block A");
};
} else {
block = ^ {
NSLog(#"Block B");
};
}
block();
The code is dangerous, and here is the explanation in the book:
The two blocks that are defined within the if and else statements are allocated within stack memory. When it allocates stack memory for each block, the compiler is free to overwrite this memory at the end of the scope in which that memory was allocated. So each block is guaranteed to be valid only within its respective if-statement section. The code would compile without error but at runtime may or may not function correctly. If it didn’t decide to produce code that overwrote the chosen block, the code would run without error, but if it did, a crash would certainly occur.
I don't understand the meaning of "If it didn’t decide to produce code that overwrote the chosen block, the code would run without error, but if it did, a crash would certainly occur."
Can someone explain and give examples?
The issue is similar to that of a C array being created locally to a function and then used after the function returns:
#import <Foundation/Foundation.h>
dispatch_block_t global_block;
int * global_arr;
void set_globals(void)
{
if( YES ){
global_block = ^{
NSLog(#"Summer is butter on your chin and corn mush between every tooth.");
};
int arr[5] = {1, 2, 3, 4, 5};
global_arr = arr;
}
}
void write_on_the_stack(int i)
{
int arr[5] = {64, 128, 256, 512, 1024};
int v = arr[3];
dispatch_block_t b = ^{
int j = i + 10;
j += v;
};
b();
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
set_globals();
write_on_the_stack();
global_block();
NSLog(#"%d", global_arr[0]); // Prints garbage
}
return 0;
}
The space on the stack that was used to store the values of the array may be re-used for any purpose. I use the separate function here because it most reliably demonstrates the problem. For your exact case, with the if block and the access in the same function, the compiler is still free to re-use the stack space. It may not, but you can't rely on that. You're breaking the scope rules of the language (derived from C).
As Jesse Rusak and CrimsonChris pointed out in comments, though, with a Block-type variable compiled under ARC, the Block is created on the stack like the array, but copied off the stack (to the heap) when it's stored in a strong pointer. All object pointers, including your global, are strong by default.
If you were not compiling with ARC, this would be unreliable. I can't come up with a failing example with my current compiler, but again, it's breaking the rules and the compiler is under no obligation to do what you want.
Essentially what this is saying is that if there's code running on a separate thread, and something gets assigned to the area of memory currently used by block but before the block() call, then bad things will happen.
void (^block)();
if (/* some condition *)) {
block = ^ {
NSLog(#"Block A");
}
} else {
block = ^ {
NSLog(#"Block B");
}
}
<--- another thread overwrites the **block** block
block(); <--- runtime error since **block** has been dereferenced.
I want to dynamically add code to a block variable, or merge or concatenate a block with another block. Is this possible?
One way of doing it is creating a block that calls the block to be "expanded" before performing its own functions.
For example, consider the example below that adds logging functionality to an arbitrary block passed into it:
typedef void (^MyBlock)(int);
-(MyBlock) expand:(MyBlock)nested {
return ^(int x) {
nested(x);
NSLog("The value of x = %d", x);
};
}
The cumulative effect of calling the block produced by expand: is that of invoking the original block, followed by an operation from the expanded block. You can take it further, to create an appendBlock method:
-(MyBlock) appendBlock:(MyBlock)second toBlock:(MyBlock)first {
return ^(int x) {
first(x);
second(x);
};
}
Is this possible?
No, but you can create a collection of blocks and execute them sequentially.
Sure - just create a new block, which makes use of the original in whatever compositional way you'd like. If you've got block1 and block2, you might create:
someCodeBefore = ^myBlockType(block1) {
someCode()
thatIWantBefore();
block1();
}
someCodeAfter = ^myBlockType(block1) {
block1();
someCode()
thatIWantAfterBlock1();
}
composedBlocks = ^myBlockType(block1, block2) {
block1();
block2();
}
Just make sure you're copying the blocks correctly.
Sup guys,
I'm trying to do a function that calls itself but by putting everything on one block,
As you can see, the following function is intended to be called an indefinite amount of times (until arcrandom returns a number lower than 50) and you should expect as an output a variable number of "RUNNING" messages, depending on chance.
void (^_test_closure)(void) = ^ {
NSLog(#"RUNNING");
if(arc4random() % 100 > 50) {
_test_closure();
}
};
_test_closure();
However, when running it, I get an EXC_BAD_ACCESS error and the reason I've found is that when the code tries to calls _test_closure inside of the closure it basically points to nowhere.
Does anyone know how to make the above code work?
You have to declare your block itself as a block variable:
__block void (^_test_closure)();
_test_closure = ^{
NSLog(#"Running...");
if ((arc4random() % 100) > 50) {
_test_closure();
}
}
_test_closure();
Recursion and blocks is tricky. Because a block captures all variables passed in, the variable _test_closure is not initialized yet (and clang should give you a warning:
Block pointer variable '_test_closure' is uninitialized when captured by block
).
There are several ways you can get around this, but the most obvious & simplest is to just make the block itself a __block variable (what #H2CO3 said). This allows the block to be weak-linked almost, so that when you call it again, it is properly initialized.
Another option you have is making the block a global or static, like this:
// outside of 'main', thus being a global variable
void (^blockRecurse)(int) = ^(int level) {
if (level < 0)
return;
NSLog(#"Level: %i", level);
blockRecurse(--level);
};
int main()
{
#autoreleasepool {
blockRecurse(10);
}
}
This means it's not being captured by the block, but instead it's referencing the global / static variable, which can be changed by all code equally.
It works with XCode 5 - no warnings, no retain cycles:
typedef void(^blockT)();
blockT block1;
blockT __block block1recursive;
block1recursive = block1 = ^(){
block1recursive();
};
block1();
I was surprised to see in a objective-c project, the following line codes
- (void)methodName
{
... some code...
{
... some code
}
{
... some code
}
}
What does the inner brackets stand for ? They seems not be preceded by any statement.
thanks
The brackets create a new scope. Variables defined within the scope will not persist after the end of the scope. I personally use this to separate out bits of logic to make things easier to read.
Example 1
This example demonstrates the lack of access to variables instantiated inside of a more narrowly defined scope.
-(void)blockTestA {
int j = 25;
{
int k = 5;
// You can access both variables 'j' and 'k' inside this block.
}
// You can only access the variable 'j' here.
}
Example 2
This example demonstrates how creating a new block scope allows us to have different variables with the same name. You can read more about scope here.
-(void)blockTestB {
int j = 25;
{
int j = 5;
NSLog(#"j inside block is: %i", j); // Prints '5'
}
NSLog(#"j outside of block is: %i", j); // Prints '25'
}
The inner brackets limit the scope of variables declared inside of them.
They create a block scope. Declared variables inside those blocks will not be available outside the blocks.
- (void)methodName
{
... some code...
{
int i;//the scope of i is within this block only
... some code
}
{
int i;//the scope of i is within this block only
... some code
}
}
I think it will be helpful to you.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Obj-C: __block variables
In block programin How use block as parameter
NSArray *(^blockreturnarray) (NSArray *);
blockreturnarray= ^(NSArray * a)
{
NSLog(#"%#",a);
return a;
};
blockreturnarray(array1);
IN simple my Question I have one block create and also another block create So how In one block pass the parameter Block.
This can get hairy so I would use typedef's and it may look something like this
typedef void (^basicBlock)(void);
typedef void (^blockAcceptingBlock)(basicBlock);
Then
basicBlock block = ^ {
NSLog(#"Called from block passed as param");
};
blockAcceptingBlock parentBlock = ^(basicBlock childBlock) {
childBlock();
};
parentBlock(block);
Without the typedef's it gets a bit noisy in the definitions with all the round braces (keep in mind that these are simple blocks so the defs shown are still fairly readable)
void (^block)(void) = ^ {
NSLog(#"Called from block passed as param");
};
void (^parentBlock)(void (^childBlock)(void)) = ^(void (^childBlock)(void)) {
childBlock();
};
parentBlock(block);
Both output
#=> 2012-04-27 11:39:12.798 Untitled[19725:707] Called from block passed as param