Can I declare variables inside an Objective-C switch statement? - objective-c

I think I'm going blind, because I can't figure out where the syntax error is in this code:
if( cell == nil ) {
titledCell = [ [ [ TitledCell alloc ] initWithFrame:CGRectZero
reuseIdentifier:CellIdentifier ] autorelease
];
switch( cellNumber ) {
case 1:
NSString *viewDataKey = #"Name";
etc...
When I try to compile it, I'm getting an Error: syntax error before '*' token on the last line.
Sorry for such a basic question, but what am I missing?

I don't have a suitable Objective-C compiler on hand, but as long as the C constructs are identical:
switch { … } gives you one block-level scope, not one for each case. Declaring a variable anywhere other than the beginning of the scope is illegal, and inside a switch is especially dangerous because its initialization may be jumped over.
Do either of the following resolve the issue?
NSString *viewDataKey;
switch (cellNumber) {
case 1:
viewDataKey = #"Name";
…
}
switch (cellNumber) {
case 1: {
NSString *viewDataKey = #"Name";
…
}
…
}

You can't declare a variable at the beginning of a case statement. Make a test case that just consists of that and you'll get the same error.
It doesn't have to do with variables being declared in the middle of a block — even adopting a standard that allows that won't make GCC accept a declaration at the beginning of a case statement. It appears that GCC views the case label as part of the line and thus won't allow a declaration there.
A simple workaround is just to put a semicolon at the beginning of the case so the declaration is not at the start.

In C you only can declare variables at the begining of a block before any non-declare statements.
{
/* you can declare variables here */
/* block statements */
/* You can't declare variables here */
}
In C++ you can declare variables any where you need them.

You can create a variable within a switch statement but you will have to create it within a block so that the scope of that variable is defined.
Example:
switch(number){
case 1:
{
// Create object here
// object is defined only for the scope of this block
}
break;
case 2:
{
// etc.
}
break;
default:
break;
}

Might it not be valid to declare a variable within a switch block?

How to solve the warning:
1.Insert one ; in the first line of your case block
2.Put codes inside braces

Related

Can you append code to an Objective-C block variable?

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.

Objective-c refer variable outside of block

I'd like to refer variable which I define inside of if block at outside of if else block. How can I? If it can't be, do I have to write same long code (they can't be function or method) inside if block and else block?
if (aTrack.compilation)
{
NSString *artistName = NSLocalizedString(#"compilations", #"");
}
else
{
NSString *artistName = aTrack.artist
}
NSLog(#"%#",artistName);
What #nickfalk and #CRD posted is good but you can also (for such easy statements) use a ternary operator which in this case will look like this:
NSString *artistName = aTrack.compilation ? NSLocalizedString(#"compilations", #""): aTrack.artist;
NSLog(#"%#",artistName);
It is a matter of style but I would go this way for this simple example as my code is in one line
The lifetime of an ordinary (non static) variable declared in a block is just that block, any nested blocks, etc. This is part of the standard lifetime and visibility rules of (Objective-)C(++).
Just declare the variable before the if/else and assign values to it in each block.
This will do what you want:
NSString *artistName;
if (aTrack.compilation){
artistName = NSLocalizedString(#"compilations", #"");
} else {
artistName = aTrack.artist;
}
NSLog(#"%#",artistName);
Also have a look at CRD's reply as this is really basic knowledge and you really need to understand this. (Also, as viperking noticed in my example, there was a terminating semicolon missing in your original code...)
Viperking has a nice example using the the ternary operator. It might be a bit alien at first but is rather nice when you wrap your head around it. a third solution would be
NSString *artistName = aTrack.artist;
if (aTrack.compilation){
artistName = NSLocalizedString(#"compilations", #"");
}
NSLog(#"%#",artistName);
For more complex scenarios I would advice against it, but for an example with two possible cases and one single string it would be quite legible. I'd also advice using nil rather than an empty-string for the localizedString comment.

Objective-C Blocks, Recursion Fails

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();

What does this syntax with brackets only mean?

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.

Objective C switch statements and named integer constants

I have a controller which serves as a delegate to two scrollviews which are placed in view managed by aforementioned view controller.
To distinguish between two scroll views I'm trying to use switch statement (instead of simple pointer comparison with if statement). I have tagged both scroll views as 0 and 1 like this
NSUInteger const kFirstScrollView = 0;
NSUInteger const kSecondScrollView = 1;
When I try to use these constants in a switch statement, the compiler says that case statements are not constants.
switch (scrollView.tag) {
case kFirstScrollView: {
// do stuff
}
case kSecondScrollView: {
// do stuff
}
}
What am I doing wrong?
This can be solved through the use of an anonymous (though not necessarily so) enum type:
enum {
kFirstScrollView = 0,
kSecondScrollView = 1
};
switch (scrollView.tag) {
case kFirstScrollView: {
// do stuff
}
case kSecondScrollView: {
// do stuff
}
}
This will compile without errors.
This is because case statement requires constant expression. Now in C and thus in Obj-C making a variable const does not create a true constant. Thus you are getting this error. But if you use C++ or Obj-C++ then this will work.
Some more hint is available here and here.