Restricting what is passed into a method - iphone

Right lets say I have a method something like :
- (void)doStuff:(NSString *)doStuffWith;
Can I make it so that doStuffWith will only accept certain words like lets say "DoSomething1" and "DoSomething2", so when i call it like :
[self doStuff:#"DoSomething1"];
it will run but if I call it like :
[self doStuff:#"HelloWorld"];
it will give a warning or something?

You should use an enum, like:
typedef enum {
MyStuffOne,
MyStuffTwo,
MyStuffThree
} MyStuff;
- (void)doStuff:(MyStuff)stuff;
thus you will be able to pass only "MyStuff" (MyStuffOne, MyStuffTwo, MyStuffThree)... these are integers and if you want to play with strings, in your method you have to do something like:
NSString *string;
switch (stuff)
{
case MyStuffOne:
string = #"StuffOneString";
break;
default:
...
}

If you need to limit amount of possible values, you should use enumeration data type instead of NSString

Why not just add an if statement into the method like this
- (void)doStuff:(NSString *)doStuffWith{
if([doStuffWith isEqualToString:#"DoSomething1"]){
//do whatever you want here
}else{
//add your warning here
}
}
That should work fine

You could create a method that checks if a word is valid and then assert that method returns true. That would then crash the app if a programmer ever called the method with a bad string, but wouldn't really help if users are able to enter strings themselves. Also, if you use the default project settings, assertions only happen when building with the Debug configuration.
For example:
static NSSet* __validStrings = nil;
- (BOOL)checkString:(NSString*)string
{
if( [string length] == 0 ) return NO;
static dispatch_once_t token;
dispatch_once(&token, ^{
// build the list of valid words once, or load from a plist or something
// if they are very large or change often
NSArray* validWords = [NSArray arrayWithObjects:#"valid", #"doSomething", #"etc.", nil];
__validStrings = [[NSSet alloc] initWithArray:validWords];
});
return [__validStrings containsObject:string];
}
// your doStuff implementation
- (void)doStuff:(NSString*)doStuffWith
{
// This will crash the program and give you debugging information if doStuffWith
// is not in your string list
NSAssert1( [self checkString:doStuffWith], #"invalid string: %#", doStuffWith );
// continue on with your method implementation...
}

Related

Problem with stringByEvaluatingJavaScriptFromString

I wanna to know abt "stringByEvaluatingJavaScriptFromString".
According to the apple doc.
"The result of running script or nil if it fails."
I have a method in which inside a for loop i'm passing a string value to stringByEvaluatingJavaScriptFromString. what will it do ?? will this get response for every string or something else will happen??
I was doing like this...
- (void)loadWithStartPoint:(NSString *)startPoint endPoint:(NSMutableArray *)endPoints options:(UICGDirectionsOptions *)options {
for (int idx = 0; idx < [endPoints count]; idx ++)
{
NSString *msg = [NSString stringWithFormat:#"loadDirections('%#', '%#', %#)", startPoint, [endPoints objectAtIndex:idx], [options JSONRepresentation]];
[googleMapsAPI stringByEvaluatingJavaScriptFromString:msg];
}
}
Inorder to get response to every string what should I do? I think that it should get response to every string that i'm passing here. Please tell me what m doing wrong and how would i manipulate my code to get response to all strings that m passing.
Any help would be appreciated .Thnx in advance.
You should make your javascript method to return a default string or something if even an error occurs in the script.
function loadDirections()
{
try
{
//Your code goes here which returns some result
}
catch(err)
{
var errorFlag = "ERROR";
return errorFlag;
}
}
and then you should alter your Objective C code like below
NSString *msg = [NSString stringWithFormat:#"loadDirections('%#', '%#', %#)", startPoint, [endPoints objectAtIndex:idx], [options JSONRepresentation]];
if([msg isEqualToString:#"ERROR")
{
//Do some error handling
}
else
{
//Your actual code goes here
}
Update for the comment:
To solve your javascript asynchronous problem either (1) you can change your design and call the javascript method only after getting response from the first call (2) or else you can use NSTimer which will will give a little time for the execution. In my opinion changing your design as per the first option would be perfect.
Have a look at these questions
stringByEvaluatingJavaScriptFromString doesn't always seem to work
return value javascript UIWebView

Pass a block of code?

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

how to define a returning NSString function in Objective-C / Xcode using a temporary variable?

I would like to define the following function in Objective-C. I have provided pseudo-code to help illustrate what I'm trying to do.
PSEUDOCODE:
function Foo(param) {
string temp;
if(param == 1) then
temp = "x";
else if(param == 2) then
temp = "y";
else if(param == 3) then
temp = "z";
else
temp = "default";
end if
return temp;
}
For some reason if I do this... the variable who I assign it to results in a "BAD Access" error.
I don't know what the difference between:
static NSstring *xx;
or the non-static:
NSString *xx;
declarations are, and how or why I would want to use one over the other.
I also do not fully understand the initializers of NSString, and how they differ. For example:
[[NSString alloc] initWithString:#"etc etc" ];
or the simple assignment:
var = #""
or even:
var = [NSString stringWithString:#"etc etc"];
Can you give me a hand please?
So far, using the NSString value returned from functions like those listed above, always causes an error.
static NSstring *xx;
That declares a statically allocated variable, much like it does in C.
NSstring *xx;
Inside a method that declares a normal local stack variable, just as it does in C.
As you should be aware, the difference between the two is that the first will keep its value between invocations of the function (and can cause trouble if the function is called from multiple threads).
[[NSString alloc] initWithString:#"etc etc"]
That creates a new NSString object, with the contents etc etc. This may or may not be the same as any other NSString object in your program with the same contents, but you don't have to care. Memory management wise, you own it, so you are responsible for ensuring that you eventually call release or autorelease on it to avoid leaking memory.
#"etc etc"
[NSString stringWithString:#"etc etc"]
Those are basically the same. Both give you an NSString object with the contents etc etc. This may or may not be the same as any other NSString object in your program with the same contents, but you don't have to care. Memory management wise, you do not own it, so you must not call release or autorelease on the object unless you first took ownership by calling retain. Also, since you do not own it, you can use it within your method, pass it as a parameter to other methods, and even use it as the return value from your method, but you may not store it in an ivar or static variable without taking ownership by calling retain or making a copy (with copy).
Also, note that "" and #"" are very different. The first gives you a const char * exactly as it does in C, while the second gives you an NSString object. Your program will crash if you use a const char * where the code expects an NSString object.
You can do it this way:
- (NSString *)functionName:(int)param {
NSString *result = nil;
switch (param) {
case 1:
result = [NSString stringWithString:#"x"];
break;
case 2:
result = [NSString stringWithString:#"y"];
break;
case 3:
result = [NSString stringWithString:#"z"];
break;
default:
result = [NSString stringWithString:#"defaultv"];
break;
}
return result;
}
Post real code, not pseudo code, as it makes it much easier to answer your question in concrete terms.
Given that you indicate that you are quite new to Objective-C, I would suggest starting with the language guide and then moving on to the memory management guide.

Using va_list and getting EXC_BAD_ACCESS

Similar to how NSLog takes variable argument list, I want to create my own method.
I have my method declared like this but when I try to access the 'args' variable, I get an EXEC_BAD_ACCESS. What is that I'm not doing correctly here?
- (void)info:(NSString *)formatString, ...
{
va_list args;
va_start(args, formatString);
NSLog(#"formatString value: %#", formatString);
// The following line causes the EXEC_BAD_ACCESS
NSLog(#"args value: %#", args);
// This is what I'm trying to do:
NSLog(formatString, args);
va_end(args);
}
I was following the 'va_list in Cocoa' section from this blog:
http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html
There are a couple of mistakes in your code. Firstly, args cannot be printed directly as is. It is a reference to several arguments, and trying to print it using NSLog("%#") will not work. What you can do is use NSLogv() to print it (e.g. NSLogv(format, args);) instead.
Or you can do what I do, and use this function:
void print (NSString *format, ...) {
va_list args;
va_start(args, format);
fputs([[[[NSString alloc] initWithFormat:format arguments:args] autorelease] UTF8String], stdout);
va_end(args);
}
The "%#" format directive takes an argument, interprets it as an Objective-C object and sends it the "description" selector. That needs to return a NSString which is printed.
So you code is trying to execute ‘[args description]‘, but args is not a Objective-C object, it's of type ‘va_list‘. Hence the exception.
See your link, the implementation of "setContentByAppendingStrings:" shows how do get the arguments out of your va_list.
Morning lads,
I just have been confronted with the very similar issue. Here is what I was doing:
+ (void) l:(D3LogLevel)p_logLevel s:(NSString *)p_format, ...
{
if (p_logLevel >= logLevel) {
va_list v_args;
va_start(v_args, p_format);
NSLog(#"[%d] %#", p_logLevel, [NSString stringWithFormat:p_format, v_args]);
va_end(v_args);
}
}
Which, as DarkDust here has described accurately, uses v_args as an Objective-C object when it is not. Here is the culprit call:
[NSString stringWithFormat:p_format, v_args]
Hence the modification to take a va_list:
[[NSString alloc] initWithFormat:p_format arguments:v_args]
Making use of the appropriate method initWithFormat which signature is:
- (id)initWithFormat:(NSString *)format arguments:(va_list)argList
We can see that the type is right and everything becomes crystal clear. The full rewritten method is then:
+ (void) l:(D3LogLevel)p_logLevel s:(NSString *)p_format, ...
{
if (p_logLevel >= logLevel) {
va_list v_args;
va_start(v_args, p_format);
NSLog(#"[%d] %#", p_logLevel, [[NSString alloc] initWithFormat:p_format arguments:v_args]);
va_end(v_args);
}
}
It works like a charm!

hot do i make a comparison of integers and values of text box in Objective C

i'm new to iphone programming and i encountered/noticed some problems while i was coding. which was after i typed a statement like
if (label.text > label2.text) {
do something...}
however, after typing my application can be compiled and run however when i try to validate it by comparing the values, my specified actions can run and i can change my image view image, however the conditions is not true but the specified actions can be run. Do enlighten me thanks! i will post my codes at the bottom, do comment if you spot any better practices? thanks once again.
Oh and how can i specify to check in my label that the default value is not "Label" or empty because i would like the values to be populated with number before commencing.
-(IBAction) beginMatch {
if (resultP1.text, resultP2.text = #"Label") {
errorMsg.text = #"Please Press Roll (:";
}
else
if (resultP1.text > resultP2.text) {
MG = [MainGameController alloc];
MG.player1 = playerName.text;
}
else {
MG.player1 = playerNameP2.text;
}
[self.view addSubview:MG.view];
}
this is one example that it does not work i have another one which is below.
-(IBAction) btn:(id) sender {
ptc = [playerTurnController alloc];
if (ptc.player1name = MGplayerName.text) {
if (lblDiceResultP1.text > lblDiceResultP2.text) {
img.image = [UIImage imageNamed:#"yellow.png"];
}
else if (ptc.player2name = MGplayerName.text) {
img.image = [UIImage imageNamed:#"Blue.png"];
}
}
}
Thank you.
Your code contains quite a few errors. You're trying to compare NSString values with ">", you're using the comma operator and = operator incorrectly, and you're allocating new objects in (what look to be) the wrong places.
You really should work your way through the introductory documentation on Apple's developer website first:
Learning Objective-C: A Primer
and
Your First iPhone Application
In here you're comparing string (alphabetically) addresses:
lblDiceResultP1.text > lblDiceResultP2.text
You probably want to extract NSNumbers of out the strings and compare the numeric values.
This here is an assignment and not a comparison:
ptc.player2name = MGplayerName.text
You probably meant to use == which is also wrong.
NSStrings are compared with the isEqualToString e.g.
NSString * s1 = #"String One";
NSString * s2 = #"String Two";
if([s1 isEqualToString:s2])
// do something when strings are equal