Writing a function that accepts NSString and floats - objective-c

I'm trying to do an iPad application.
In one of the functions, I'm supposed to return a value depending on the string input.
Here's the code:
float myFunction(float t, NSString * Color){
if ([Color isEqual:#"blue"])
return t*100;
else if ([Color isEqual:#"red"])
return t*2;
else
return t;
}
But somewhere my program does something wrong. Since I don't get the number I'm expecting. Is it possible that this code is the missing link?
I get no errors or warnings.

instead of isEqual you should use isEqualToString
and change Color to color
Try if this makes a difference (if not the problem lies elswhere in the code):
float myFunction(float t, NSString * color){
if ([color isEqualToString:#"blue"])
return t*100.0;
else if ([color isEqualToString:#"red"])
return t*2.0;
else
return t;
}
If you want your function to look more like obj-c method then use:
-(CGFloat) myFunctionForT:(CGFloat)t andColor:(NSString *)color {
if ([color isEqualToString:#"blue"])
return t*100.0;
else if ([color isEqualToString:#"red"])
return t*2.0;
else
return t;
}
The main reason for using color instead of Color and 100.0 instead of 100 is that a bit more discipline in coding saves you hours and hours of debugging time.

Related

cannot call function from other function

I need to get abs. the value of NSDecimalNumber. And I use pre-defined function for that:
- (NSDecimalNumber *)aDN: (NSDecimalNumber *)num {
if ([num compare:[NSDecimalNumber zero]] == NSOrderedAscending) {
// negative value
return [[NSDecimalNumber zero] decimalNumberBySubtracting:num];
} else {
return num;
}
}
However when I try to call it from other function. Namely
- (void) prepareForSeque....
wordD=aDN(number);
I get the error: " Implicit declaration of function 'aDN' is invalid in C99'
Can you give me some pointers how to troubleshoot this?
Thanks in advance.
You appear to be confusing languages. It looks like you should have
wordD = [self aDN:number];
(Assuming the class hosting the method is the current class and not a category on decimal number which might make more sense...)

Print string representation of an enum, NSLog

I am trying to NSLog some enums I have. For example this piece of code prints the integer representation of the enum, but I want it to output the actual string name, in this case MON. How can I do that?
#import <Foundation/Foundation.h>
int main(void)
{
typedef enum {
SUN,
MON,
TUES
} DAYS;
DAYS d = MON;
NSLog(#"%#", d);
return 0;
}
The LLDB debugger will show the string identifiers. So instead of using NSLog you could use a breakpoint with a debugger command action ("p d" in your case) and set the breakpoint to automatically continue after evaluating.
You can configure a breakpoint by right-clicking on the blue marker.
Not easily. The string identifier for an enum value is for the developer, but internally it's simply a value with a particular type (in your example, DAYS).
You could write a translation method, to return the name of the enum value, e.g
- (NSString*)nameForDay:(DAYS)day {
switch (day) {
case SUN:
return #"SUN";
break;
case MON:
return #"MON";
break;
case TUES:
return #"TUES";
break;
default:
return nil;
break;
};
return nil;
}
It's a nasty way of doing it, as it's not wholly resilient to the enum values changing, but its a way to associate a string with an enum value.

XCode Analyzer Reporting Garbage Ob

- (UIImage*)returnRandomBackground{
int selectedImage = (arc4random() % 4) + 1;
UIImage *imageToReturn;
if (selectedImage == 1) {
imageToReturn = Image1;
}
else if (selectedImage == 2) {
imageToReturn = Image2;
}
else if (selectedImage == 3) {
imageToReturn = Image3;
}
else if (selectedImage == 4) {
imageToReturn = Image4;
}
return imageToReturn;
}
UnDefined or garbage value returned to caller.
To me this code looks legal, whats wrong with it?
The compiler is not smart enough to deduce that (arc4random() % 4) + 1 is always in the range [1..4]. Just initialize imageToReturn to null and the warning will go away.
Note: if you use this, and at some point you change the modulus in the random number generation, you're liable to return nulls to the caller (with no warning from your friendly compiler) if you don't adapt your if/else sequence accordingly.
If that is not something you want, use an assertion somewhere (possibly by adding a "catch-all" else clause and asserting there).
Also, consider using a switch block here instead of the if/else sequence, that's the most natural construct here in my opinion.
The compiler will think that it's possible that there's a selectedImage = 5, which means there's a possibility of imageReturn remaining unassigned. You'll have to change the last else if to just an else to make sure imageReturn is always assigned.
Look at what the code would do if selectedImage = 5. It'll break.
Alternatively - you could just initialise imageToReturn
UIImage *imageToReturn = nil;
Then, whatever happens, you won't be returning a garbage value.

correct way to send messages to (id *) variables or with (id *) arguments

I have a Core Data validation method I wrote that will not compile. I can modify the method so it compiles... but then I get runtime errors. The two versions of the method are below (notice the missing '*' in the second version). This version compiles but gives the runtime error "+[NSCFNumber doubleValue]: unrecognized selector sent to class 0x7fff70a448e8":
- (BOOL)validateInitialValue:(id *)value error:(NSError **)error {
if ( *value == nil ) {
return YES;
}
if ( [*value doubleValue] < 0.0 ) {
return NO;
}
return YES;
}
This version gives compiler warnings and errors (see warnings and errors below):
- (BOOL)validateInitialValue:(id *)value error:(NSError **)error {
if ( value == nil ) {
return YES;
}
if ( [value doubleValue] < 0.0 ) {
return NO;
}
return YES;
}
compiler errors and warnings:
warning: Semantic Issue: Receiver type 'id *' is not 'id' or interface pointer, consider casting it to 'id'
warning: Semantic Issue: Method '-doubleValue' not found (return type defaults to 'id')
error: Semantic Issue: Invalid operands to binary expression ('id' and 'double')
I finally figured that the problem may be in the calling code:
- (void)setInitialValue:(NSNumber *)initialValue {
[self willChangeValueForKey:#"initialValue"];
[self validateValue:initialValue forKey:#"initialValue" error:nil];
[self setPrimitiveInitialValue:initialValue];
[self didChangeValueForKey:#"initialValue"];
}
I changed the caller to use an '&' before initialValue and used the first version of the method, and everything worked. So the new calling code has the one line changed to be this:
[self validateValue:&initialValue forKey:#"initialValue" error:nil];
But is it really necessary to have the '&'?? setPrimitiveInitialValue doesn't use the '&'. I feel like my understanding of Objective-C is just not developed enough yet and all you gurus out there will find this a trivial question with a very straight forward answer.
id itself represents a pointer. So when you use id * you are actually referring to a pointer-to-a-pointer. The first part of this excellent tutorial explains this concept.
Chances are, this is what you are looking for:
- (BOOL)validateInitialValue:(id)value error:(NSError **)error {
if ( value == nil ) {
return YES;
}
if ( [value doubleValue] < 0.0 ) {
return NO;
}
return YES;
}
You're right that the problem is the calling code. id * indicates a pointer to an id value. An object variable by itself is an id, so you want a pointer to that variable, which is what you get with the &.
The reason you pass a pointer is so that, if your validation method knows of a way to modify the value to make it valid, it can return YES and also return the valid object (by setting the variable). So, for example, if numbers less than 1 should be clamped to 0, you might do:
- (BOOL)validateInitialValue:(id *)value error:(NSError **)error {
if ( *value == nil ) {
return YES;
}
if ( [*value doubleValue] < 0.0 ) {
return NO;
}
if ( [*value doubleValue] > 0.0 && [*value doubleValue] < 1.0 ) {
*value = [NSNumber numberWithInt:0];
}
return YES;
}
setPrimitiveValue: doesn't need to set variables in the calling context, so it just takes an id. (Very few methods work like validateValue:forKey:error:. Generally, they'll do it that way if they want to return a BOOL to indicate whether they changed something, but they still need a way to return the changed value as well.)

convert id into enum using objective-c

I am trying to implement a simple method, however I am still quite a newbie on objective-c.
I have this simple method which is trying to convert from an id to a specific value in enum, if matched.
This is the enum
typedef enum {
DXTypeUnknown = 0,
DXDatasource = 1,
DXGroup = 2
} DXPropertyType;
And this is the relevant method:
-(DXPropertyType)typeFromObject:(id)_type {
int _t = [_type intValue];
switch (_t) {
case DXDatasource:
return [NSNumber numberWithInt:DXDatasource];
case DXGroup:
return [NSNumber numberWithInt:DXGroup];
default:
return [NSNumber numberWithInt:DXTypeUnknown];
}
}
The very first check I would to implement is if the id can be converted to an int, then see if it falls in the two relevant categories group or datasource, or return a default value if not. Could you tell me if the switch/case I implemented is a proper solution or not ?
I would like also this method not to causing crash of an application, so what could be advisable to check, keeping in mind that in any case the default value is to be returned.
thanks
[EDIT]
I forgot to say that this value is going to be stored in a field of a NSManagedObject, which by CoreData restriction can be an NSNumber, so probably there's a better solution instead of an enum.
It might be a good idea to include this code to check if the id can be used:
if (![_type respondsToSelector:#selector(intValue)])
return nil;
However, if you'll always pass a NSNumber go ahead and declare the method as:
- (DXPropertyType)typeFromObject:(NSNumber)_type;
In your code, you're returning a NSNumber. I don't think that's what you really
want, as you'd be doing nothing with the NSNumber passed. Return the enum
item:
-(DXPropertyType)typeFromObject:(id)_type {
if (![_type respondsToSelector:#selector(intValue)])
return nil;
int _t = [_type intValue];
switch (_t) {
case DXDatasource:
return DXDatasource;
case DXGroup:
return DXGroup;
default:
return DXTypeUnknown;
}
}
And then this can be simplified to:
- (DXPropertyType)typeFromObject:(id)_type {
if ([_type respondsToSelector:#selector(intValue)]) {
int t = [_type intValue];
DXPropertyType property_t;
if (t >= 1 && t <= 2)
property_t = t;
else
property_t = DXTypeUnknown;
return property_t;
}
return nil;
}
Your switch statement is a good solution and will not cause a crash.
However, your method returns a NSNumber when it expects a different return. I suggest changing the method to
-(NSNumber)typeFromObject:(id)_type
You specify that your method returns an enum, but you return objects. So either return the enum values or specify the return type to be NSNumber *.
A different solution could be using singleton objects instead of an enum, but that's probably more work than it's worth. Think [NSNull null].