For example, I have a huge switch control structure with a few hundred checks. They're an animation sequence, which is numbered from 0 to n.
Someone said I can't use variables with switch. What I need is something like:
NSInteger step = 0;
NSInteger i = 0;
switch (step) {
case i++:
// do stuff
break;
case i++:
// do stuff
break;
case i++:
// do stuff
break;
case i++:
// do stuff
break;
}
The point of this is, that the animation system calls a method with this big switch structure, giving it a step number. I want to be able to simply cut-copy-paste large blocks and put them in a different position inside the switch. for example, the first 50 blocks to the end.
I could do that easily with a huge if-else structure, but it would look ugly and something tells me switch is much faster.
How to?
By not doing it this way. A case label has to be a constant.
Here's one way to do this that might be better:
Define a selector for each thing you want to do e.g.
-(void) doOneThing;
-(void) doAnotherThing;
// etc
Put them in an array:
SEL anArray[] = { #selector(doOneThing), #selector(doAnotherThing) /* other selectors */, NULL };
And then you can just iterate through them.
SEL* selPtr = anArray;
while (selPtr != NULL)
{
[self performSelector: *selPtr];
selPtr++;
}
Related
I want to be able to identify a group of objects for a CCSpriteBatchNode, but also identify a sub group of that group. To do something similar to this
CCArray *listOfGameObjects = [sceneSpriteBatchNode children];
for (GameObject *tempObject in listOfGameObjects) {
if ([tempObject tag] == kBottleTagValue) {
//make bottle yellow
if ([tempObject tag] == kBrokenBottleTagValue) {
//also make bottle smaller
}
}
}
In the example all bottles would be turned yellow, and if the bottle was also tagged as broken it would be made smaller. So the broken bottle would need to be tagged with kBottleTagValue and kBrokenBottleTagValue. Is there away to do this? cause when I try to just add two tags it fails.
Yes you can do that using bit masking.
For example define your tag like that:
enum
{
kBottleTagValue = 1 << 0;
kBrokenBottleTagValue = 1 << 1;
};
Then tag your sprite:
[yoursprite setTag:kBottleTagValue|kBrokenBottleTagValue];
To finish you can check if your sprite is a broken bottles by doing something like that:
CCArray *listOfGameObjects = [sceneSpriteBatchNode children];
for (GameObject *tempObject in listOfGameObjects)
{
if ([tempObject tag] & kBottleTagValue)
{
//make bottle yellow
if ([tempObject tag] & kBrokenBottleTagValue)
{
//also make bottle smaller
}
}
}
I hope it'll help you.
Using bitmasking is overkill and there's no need to abuse the tag property.
Speaking of properties: You can add a boolean property to your class and use if ([tempObject isClass:[BottleClass class]]) for the first logic gate.
I don't actually know Cocos2d but based on a quick reading, I guess you've descended GameObject, by whatever roundabout route, from CCNode? In that case the tag field is an integer. You can't store multiple values to it, but you could use it as a bit field. For example:
#define kTagValueBottle 0x0001
#define kTagValueBroken 0x0002
#define kTagValueAnotherAttribute 0x0004
#define kTagValueAThirdAttribute 0x0008
#define kTagValueAFourthAttribute 0x0010
/* etc */
You'd then assign the type as, for example:
object.tag = kTagValueBottle | kTagValueBroken;
So that computes the bitwise OR of kTagValueBottle and kTagValueBroken, and stores it as the new tag. You could also add a property at any time using a bitwise OR:
object.tag |= kTagValueBroken;
Or remove by using a bitwise AND with the inverse mask:
object.tag &= ~kTagValueBroken;
You'd replace your direct comparison tests with testing individual bits via a bitwise AND:
// if ([tempObject tag] == kBottleTagValue) // old test
if ([tempObject tag] & kBottleTagValue) // new test
This is the same sort of system that Apple use for properties like autoresizingFlags on UIView.
If you can handle reading example code in PHP rather than Objective-C, this seemed to be the most helpful article that I could find quickly through Google, though admittedly from slender pickings.
Is it possible to pass a block of code, for example:
int i = 0;
while (i < [array count]) {
//Code to pass in here
i++;
}
Reason being that i need to perform various actions on every item in an array at different times, it'd be nice to have 1 method and pass it a block of code to run.
Have you considered using blocks. It's just an idea and not working or compilable code
typedef int (^block_t)();
-(void) methodName:(block_t) code_block
{
int i = 0;
while (i < [array count]) {
code_block() //Code to pass in here
i++;
}
block_t youCode = ^{ NSLog("Just an example"); }
[self methodName:youCode];
You can definitely iterate an array and process a block of code on it. This feature has been a standard part of Objective C since 2.0, iOS since 4.0 and in addition was included in Snow Leopard. If you look up the NSArray class reference you will find the following functions:
enumerateObjectsAtIndexes:options:usingBlock:
Executes a given block using the
objects in the array at the specified
indexes.
enumerateObjectsUsingBlock: Executes a
given block using each object in the
array, starting with the first object
and continuing through the array to
the last object.
enumerateObjectsWithOptions:usingBlock:
Executes a given block using each
object in the array.
You can define the code block to be executed globally in your implementation file, or in place where its needed. You can find some good examples on block programming here: http://thirdcog.eu/pwcblocks/.
It sounds like you want "blocks", which are a feature of Objective-C similar to "lambdas", "anonymous functions" and "closures" in other languages.
See Apple's documentation: Blocks Programming Topics
You should put the code into a method and call the method like so:
-(void)foo{
int i = 0;
while (i < [array count]) {
[self myMethod];
i++;
}
}
-(void)myMethod{
//Code to pass in here
}
You could also store the method as a variable allowing you to change which method is called
SEL methodToCall = #selector(myMethod);
-(void)foo{
int i = 0;
while (i < [array count]){
[self performSelector:methodToCall];
i++;
}
}
-(void)myMethod{
//Code to pass in here
}
first of all you could give a more detailed example.
second you could take a look at Action or Func in case you need a return message (well something equivalent for obj-C if exists)
but again, not understanding what you need to do, it is hard to give you an answer
cause from you Q i could understand as #Trevor did that you need to run a method:
int i = 0;
while (i < [array count]) {
if (i % 2)
EvenMethod(array[i])
else
OddMethod(array[i])
i++;
}
If you can live with 4.0+ compatibility, I highly recommend the use of blocks.
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
gives you all you need and you can define your block right where you need it. There are some other variants of this, too. Please refer to the 'NSArray' documentation.
Why not just define functions that do what you want, and call them at various times with the items in your array as arguments?
If your concern is that you don't want redundant code with multiple while loops you have here, you could write a function that takes an array and a function as an argument, then applies that function to every element in the array.
Adapted from here:
//------------------------------------------------------------------------------------
// 2.6 How to Pass a Function Pointer
// <pt2Func> is a pointer to a function which returns void and takes an NSObject*
void DoForAllItems( NSArray* array, void (*pt2Func)(NSObject*) )
{
int i = 0;
while (i < [array count]) {
(*pt2Func)([array objectAtIndex:i]);
i++;
}
}
// 'DoIt' takes an NSObject*
void DoIt (NSObject* object){ NSLog(#"DoIt"); }
// execute example code
void Pass_A_Function_Pointer()
{
NSArray* array= [NSArray arrayWithObjects:#"1", #"2", nil];
DoForAllItems(array, &DoIt);
}
http://thirdcog.eu/pwcblocks/#objcblocks
Blocks were added recently to Objective-C I hope they have found their way to the Iphone also.
and it was anwered here also before:
Using Objective-C Blocks
Regards
If you are using Objective C++, I would strongly recommend function objects (even over blocks). They have a nice syntax, are easy to use, and are supported everywhere on modern C++ compilers (MSVC++11, GNUC++11, and LLVMC++11):
void go( function<void (int arg)> func )
{
int i = 0;
while (i < [array count]) {
//Code to pass in here
func( i ) ; // runs func on i
i++;
}
}
calling it:
go( []( int arg ){ // the square brackets are the "capture"
printf( "arg was %d\n", arg ) ; // arg takes on whatever value was passed to it,
// just like a normal C function
} ) ;
I was wondering what would be the best way to implement a morse code generator. The way it would work is the user types in a word or phrase and then that NSString would be passed into my method as an argument for processing. The way I want to process is to loop through each character in the string and then play the correct sequence of tones as I go. I've already implemented the mechanism in a separate class.
I've already tried so many different methods but they all seem to introduce some sort of problem. Things i've tried so far are: creating an NS Timer, using delaying method calls (performSelector:withObject:afterDelay).
Below is an example of the method i'm using to delay, but the problem is that if the user types in say "aaaa" only the first "a" will play because the entire loop will have been completed before the morse sequence is even done generating. It would be more ideal if the loop could wait for the switch case to finish before continuing.
Anyways this is the way that i thought it should be implemented but it doesn't seem to be working, so maybe there is a better way of implementing the morse code generator or i'm just missing a step so if anyone can please help me figure this out i'd really appreciate it.
- (NSString *)convertTextToMorse:(NSString *)phrase {
for (int i = 0; i < [phrase length]; i++) {
unichar ch;
ch = [phrase characterAtIndex:i];
NSLog(#"Processing charachter %c",ch);
//Dot = 100ms
//Dash = 300ms
//Pause = 200ms
switch (ch) {
case 'a':
//Morse "a"
[self playDotSound];
[self performSelector:#selector(playDashSound) withObject:nil afterDelay:0.2];
break;
case 'b':
//Morse "b"
break;
default:
break;
}
}
return phrase;
}
Another option might be to run the entire thing on a separate thread:
- (NSString *) convertTextToMorse:(NSString *)phrase {
[self performSelectorInBackground:#selector(morseInternal:) withObject:phrase];
return phrase;
}
- (NSString *) morseInternal:(NSString *)phrase {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
for (int i = 0; i < [phrase length]; i++) {
if (stopMorse) {
break;
}
unichar ch = [phrase characterAtIndex:i];
NSLog(#"Processing charachter %c",ch);
switch (ch) {
case 'a':
[self playDotSound];
[self pause];
[self playDashSound];
break;
case 'b':
//Morse "b"
break;
default:
break;
}
}
[pool release];
}
- (void) pause {
[NSThread sleepForTimeInterval:0.2];
}
Notice I added a stopMorse boolean instance variable that you can set to true if you want to stop the morse code generation in the middle of the string.
As you figure out what sounds to play, add them to an array.
Play through the array and save your position in the array as you go along. If you get stuck parsing, pause, and continue right where you left off.
Alternatively, figure out how long each letter takes and use a variable for the playback delay.
NSTimers and run loop delays produce very poor Morse Code, and get worse at higher WPM. It's better to create a DSP audio synthesis engine good enough for millisecond precise drum loops (probably requiring use of the RemoteIO Audio Unit), and using that to mix down timed beeps instead of percussion instrument samples.
I was looking though a fellow developers code when I saw this ..
for (;;){
....
....
....
}
I have never seen ";;" used in a loop. What does this do exactly?
It loops forever. ';;' equates to no start value, no stop condition and no increment condition.
It is equivalent to
while (true)
{
...
}
There would usually be a conditional break; statement somewhere in the body of the loop unless it is something like a system idle loop or message pump.
All 3 parts are optional. An empty loop initialization and update is a noop. An empty terminating condition is an implicit true. It's essentially the same as
while (true) {
//...
}
Note that you it doesn't have to be all-or-nothing; you can have some part and not others.
for (init; cond; ) {
//...
}
for (; cond; update) {
//...
}
for (init; ; update) {
//...
}
Just like in C, the for loop has three sections:
a pre-loop section, which executes before the loop starts.
a continuing condition section which, while true, will keep the loop going.
a post-iteration section which is executed after each iteration of the loop body.
For example:
for (i = 1, acc = 0; i <= 10; i++)
acc += i;
will add up the numbers from 1 to 10 inclusive (in C and, assuming you use Perl syntax like $i and braces, in Perl as well).
However, nothing requires that the sections actually contain anything and, if the condition is missing, it's assumed to be true.
So the for(;;) loop basically just means: don't do any loop setup, loop forever (breaks notwithstanding) and don't do any iteration-specific processing. In other words, it's an infinite loop.
Infinite loop. A lot of the time it will be used in a thread to do some work.
boolean exit = false;
for(;;) {
if(exit) {
break;
}
// do some work
}
Infinite Loop (until you break out of it).
It's often used in place of:
while(true) { // Do something }
It's the same as
while(true) {
...
}
It loops forever.
You don't need to specify all of the parts of a for loop. For example the following loop (which contains no body, or update token) will perform a linear search of myArray
for($index = -1; $myArray[++$index] != $target;);
In my code I need to be able to jump (goto) a different case within the same switch statement. Is there a way to do this?
My code is something like this: (There is a lot of code I just left it all out)
switch (viewNumber) {
case 500:
// [...]
break;
case 501:
// [...]
break;
.
.
.
.
.
case 510:
// [...]
break;
default:
break;
}
Thank you for your time!
-Jeff
It's generally very bad practice to unconditionally jump like you're asking.
I think a more readable/maintainable solution would be to place the shared code in a method and have multiple cases call the method.
If you really want to, you can use goto to do something like:
switch(viewNumber) {
case 500:
// [...]
goto jumpLabel;
case 501:
// [...]
break;
case 502:
// [...]
jumpLabel:
// Code that 500 also will execute
break;
default:break;
}
Note: I only provided the code example above to answer your question. I now feel so dirty I might have to buy some Bad Code Offsets.
Instead of using goto, refactor your code so that the two (or more) cases that use common code instead call it in a common method.
Something like:
switch (value) {
case (firstValue):
// ...
break;
case (secondValue):
[self doSharedCodeForSecondAndThirdValues];
break;
case (thirdValue):
[self doSharedCodeForSecondAndThirdValues];
break;
default:
break;
}
// ...
- (void) doSharedCodeForSecondAndThirdValues {
// do stuff here that is common to second and third value cases
}
It wouldn't be the end of the world to use goto, though it is bad practice.
The practical reason for avoiding use of goto is that you have to search through your swtich-case tree to find that goto label.
If your switch logic changes, you'll have a messy situation on your hands.
If you pull out common code to its own method, the code is easier to read, debug and extend.
You should probably try rewrite your code, like a recursive call or just factor out common stuff and call a separate function. But as a fix and quick answer to your question you could put a label before your switch and goto it, like so
switchLabel:
switch(viewNumber) {
case 500: {
viewNumber = 501;
goto switchLabel;
}
}
Not sure of the Objective-C syntax here, but you could also try a variation thereof
int lastView = 0;
while (lastView != viewNumber)
switch(lastView = viewNumber) {
case 500: {
viewNumber = 501;
break;
}
}
which will keep on looping until the viewNumber doesn't change any more. This is still pretty much just a pretty-looking goto though.
And since we're doing gotos you could just goto into another case, as pointed out already. You could also do fancy stuff similar to Duff's device, by putting cases inside of other blocks. But that's just mad.. :)
[I'm making this answer community wiki because this doesn't actually answer the question per se]
As others have said, this is very bad style, and makes for unreadable code...
Alternatives:
Factor the common code into a separate function, and call that in 2 places.
Use fallthroughs, leave off the the break on a case and it falls through to the next one (remember, cases don't have to be in numerical order!)
if you only want part of a case to be done in the other case, protect it with an if:
as in
case 500:
.
.
.
case 501:
if(viewNumber == 501) {
.
.
.
}
.
.
.
break;