Incompatible Type for Argument 1 Objective-C - objective-c

I have a function that sets an entity within a Core Data store. I used to have all values it would be storing as type double, however now I must make it accommodate NSStrings as well. Consequently, I changed the type of the parameter the function takes in, to an id type. However, now I get the error:
error: incompatible type for argument 1 of 'numberWithDouble:'
...at the following lines:
//...
[dfm setTimeStamp:[NSNumber numberWithDouble:value]];
//...
[[fetchedObjects objectAtIndex:0] setValue:[NSNumber numberWithDouble:value] forKey:#"timeStamp"];
//...
Apparently it doesn't like the [NSNumber numberWithDouble:value] segment of each line. I was contemplating making a container class that holds an NSNumber type (doesn't Apple already have a class like this?) to get around this problem, but I thought that there has to be an easier way I am not thinking of (besides duplicating the function and changing the type of the value parameter). Any ideas? Thanks in advance!
EDIT:
Here is the function declaration:
-(void)setItemInDFMWhilePreservingEntityUniquenessForItem:(attribute)attr withValue:(id)value
attribute is merely an enum which specifies which entity to store within. The problem is that the compiler is giving me problems with value being of type id, theoretically I can pass in anything I want, and I believe the way I have it I am implying that I will be passing it as an NSNumber, but the compiler doesn't like that as that is not actually a class instance I suppose?

The problem is that the compiler is
giving me problems with value being of
type id, theoretically I can pass in
anything I want, and I believe the way
I have it I am implying that I will be
passing it as an NSNumber, but the
compiler doesn't like that as that is
not actually a class instance I
suppose?
By declaring value as id, you can pass any object you want. But why do you "suppose" that NSNumber isn't an object, when it's clearly documented as being an object? The warning isn't about passing an NSNumber instance when you've declared value as an id - that's perfectly valid, because id means "any object," and an NSNumber instance is an object. The warning comes from calling +numberWithDouble:, a method that takes a double for its first argument, and passing it value, which is declared as id - i.e. an object. You can't pass an object to a method that expects a double.
Your proposed solution, typecasting value with (NSInteger)value will silence the warning, but it won't fix the problem. The typecast simply converts the memory address the object pointer targets to an integer value. If (as your edit suggests) value is already an NSNumber object, what do you hope to gain by creating another one, or by typecasting its memory address to an integer? Just do:
[dfm setTimeStamp:value];

The problem lies with the value variable. It should be declared as a double (primitive) for this call to succeed.
edit: after rereading your question, do a check in the function on the type of value, if it is an NSString (use [value isKindOfClass:[NSString class]]) store it as such, if its not then its a double (if thats the only two types you are passing) and store it as such.

Can't you just pass the NSNumber instead of double?

Just realized that the call I was making (numberWithDouble:) was having the compiler check for a primitive, i.e. double. Changing it to the following worked like a charm:
[dfm setTimeStamp:[NSNumber numberWithInteger:(NSInteger)value]];
Thanks to those that responded!

Related

Objective-C setValue:forKey on c primitive types

I'm trying to teach myself objective-c using the big nerd ranch book, it is a really great book but certain aspects confuse me.
The current chapter is talking about using setValue:forKey function which I understand is a method defined in NSObject. The book says that you can use this on a c primitive like int or float and gives this example
I have a custom class called Appliance and in it is an integer instance variable called voltage that stores the voltage of the current appliance
I initialize a new appliance called a
appliance *a = [[appliance alloc]init];
[a setValue:[NSNumber numberWithInt:240] forKey:#"voltage"];
he then sets up a custom setter for voltage and logs the voltage when its called to prove it works
-(void)setVoltage:int(x) {
NSLog(#"setting voltage to %d",x);
voltage =x;
}
whats confusing me is that NSNumber numberWithInt returns a pointer to an NSNumber object thats stored on the heap correct? so then how does he log the integer stored in NSNumber using the %d token. I understand that would log an integer but isn't an object being passed in? furthermore I thought that since voltage was defined as an integer and not a pointer to something it couldn't hold the address to an object in its memory? or is NSNumber kind of forcing it to hold its memory address without actually having voltage being declared as a pointer?
sorry for the confusion this chapter basically kicked my butt.
The conversion between objects and scalar types is handled automatically by the Key-Value Coding methods. From the documentation:
The default implementations of valueForKey: and setValue:forKey:
provide support for automatic object wrapping of the non-object data
types, both scalars and structs.
Once valueForKey: has determined the specific accessor method or
instance variable that is used to supply the value for the specified
key, it examines the return type or the data type. If the value to be
returned is not an object, an NSNumber or NSValue object is created
for that value and returned in its place.
Similarly, setValue:forKey: determines the data type required by the
appropriate accessor or instance variable for the specified key. If
the data type is not an object, then the value is extracted from the
passed object using the appropriate -<type>Value method.
So in your case, intValue is applied automatically to the passed NSNumber
object, and the resulting integer is passed to setVoltage:.
You are correct in that you are creating an NSNumber instance and passing that. But, you're passing it to setValue:forKey: and it's doing some work for you. It's finding the appropriate setter method for voltage (setVoltage:), checking the data type and unboxing the number into an int before calling the setter.

Passing an int as "object" in NSNotification

I'm trying to send an int (which is defined in an enum) as the object in an NSNotification.
I can't do this, because I'll get an EXC_BAD_ACCESS error thrown when trying to access the object in the notify: method of the observer, since int is not of type id (I think?).
My current solution is to cast the int to an NSNumber (by using numberWithInt:). While this works it doesn't feel right or clean.
So the question is: what is the correct way to pass an int as the "object" of an NSNotification?
I guess this actually has nothing to do with this specific case, but a Cocoa thing in general :-)
You're not casting to NSNumber... you're creating a NSNumber object and setting the contents (or value) of it to your int.
And Objective C objects are what you can pass in the userInfo dictionaries of NSNotifications, so what you are doing is absolutely correct.
Now as to the crash, please edit your question to show code as to how you are creating and posting your NSNotification and what the method is that actually handles the received NSNotification you sent.
id is the generic type for objects. So if the method expects an id, you can't pass an int —it's not an object. The optimal object for numeric values is NSNumber, so you're doing it correctly.
A side note: don't use the term "casting" here. You're not casting, you're creating an object.
The correct way it what you are doing, create an NSNumber instance and pass it.
As #Michael say, you are not casting it, you are creating an instance.

What is the meaning of this Objective-C syntax?

Would someone please clarify what the difference in these two snippets would be?
I know this is instantiation:
Class *myClass = [[Class alloc] init] ....etc
but what exactly is this?
(Class *)myClass .....etc
Thanks
The second snippet is either a cast or a parameter to a method. Neither have anything to do with instantiation.
If (Class *)myClass occurs in a method declaration, it just defines what type the parameter to the method should be. For example, - (void) method:(Class *)myClass is a method that returns void and takes one argument, of type Class*.
If (Class *)myClass occurs in other code, it's a cast. Basically it says to reinterpret myClass as a pointer to an object of type Class, regardless of what its type really is. It's like casting with numbers - if x is an int, (float)x casts it as a float so you can use it in floating-point arithmetic.
Generally speaking, I'd caution you against using casting heavily with Objective-C objects. One place you will see things like this is in casting NS objects to CF objects, as in (CFURLRef)[NSURL fileURLWithPath:path]. But most often objects of different types will not cast properly.
Also, you have an error in your first snippet. It would actually be [[Class alloc] init]. You must call alloc and then init. And [init] is meaningless - it doesn't fit the [object method] syntax of Objective-C at all.
The first one, given correct syntax is instantiating, as you say.
The second one is casting a variable "myClass" to a pointer to an instance of the Class object.
The second snippet is a C-style cast. It effectively tells the compiler to treat myClass as a value of type Class* regardless of its declared type. Without the rest of the snippet (and the preceeding declaration of myClass), it's impossible to say why you would want to use the cast or what effect it would have.

Should I be casting when returning id from an objective-c method or not?

For the Objective-C gurus:
Suppose I have a simple method like so:
-(id)getValue{ return [NSNumber numberWithDouble:5.0]; }
Now, suppose within some other method I call the (id)getValue method like so:
NSNumber* myValue = [self getValue];
or what if I call it like this instead:
NSNumber* myValue = (NSNumber*)[self getValue];
The question is: Obviously these lines are equivalent but one of them utilizes an explicit cast. So what is the correct or best-practice way of doing this. It seams to me the cast is unnecessary since when it is placed in the pointer myValue, it will be type-safe at this point anyways (which is something I want) so the cast is basically pointless.
Let me just add that I'm sure people will point out: Why don't you just return (NSNumber*) from the getValue method but in my case I want to have the flexibility to return whatever I want much like the built in NSDictionary class returns id when you call: objectForKey because it allows you to place any type of NSObject or subclass inside of it. In other words my getValue method will not always be returning an NSNumber. Also consider this example is contrived because I am just concerned about whether to cast or not.
Thank you in advance,
-Ralph
The only reason to cast objects is to make the compiler happy. (Sometimes it also helps readability.) For example, you have to cast when making a property access directly on an object you're getting out of an array or dictionary:
((Foo *)[myArray objectAtIndex:0]).bar;
If you don't do the cast, the compiler can't do the property lookup, and will complain.
When you're getting an object from a method that returns id, it's impossible for the compiler to know what its actual type is. There isn't really any "type-safety", because id is a generic pointer; all the compiler can and will enforce is that the method says it returns some Objective-C object. It is perfectly happy to assign a generic pointer to any typed pointer.* (This is actually an advantage for containers, obviously.) Since the type of the variable to which you're assigning already documents the actual return type, I'd say there's no need for the cast.
As an aside, you shouldn't be calling your method getX. That has a specific meaning in Cocoa; methods which "get" something pass in a pointer to a pointer, which is then filled by the method. See -[NSArray getObjects:range:] as an example.
*The type will be enforced at run-time, of course, in the sense that sending messages to which the object does not respond will cause an error.

How do I cast id to a float?

I'm having trouble with this code:
NSRect itemFrame;
id item;
// code to assign item goes here.
itemFrame.origin.y -= [item respondsToSelector:#selector(selectedHeight)] ? [item selectedHeight] : [self defaultSelectedHeight];
This is the problematic bit:
[item selectedHeight]
The compiler is assuming that the return type is id. I though that adding a cast would fix this:
(float)[item selectedHeight]
but it doesn't work.
What am I doing wrong? (I suspect the problem is to do with resolving pointers related to id but I can't find any relevant documentation).
you want [[item selectedHeight] floatValue], assuming that the selectedHeight returns an NSNumber.
I know that below code works for iOS 6 SDK. I assume that obj object contains a float value.
id obj;
float fVal;
fVal = [obj floatValue];
You need to look at the declaration of your selectedHeight method. The problem is either that the method is returning a pointer to an object (id), or you haven't imported the header file for item in the file that contains the code snippet, so Xcode assumes it's a pointer by default.
You can't cast a pointer to a float, since they're fundamentally incompatible types. Once you get your declarations straightened out though you should be okay.
The compiler makes that kind of assumptions when multiple classes declare methods with the same name, that return different types. Since your "item" variable is typed as an "id," the compiler doesn't know which of these classes it will be sending the message to at run time, and chooses one.
To avoid this problem, you can inform the compiler what class "item" is an instance of, by declaring it with a specific type instead of the generic "id":
SomeItemClass *item;
You could also avoid it by not declaring identically-named methods that return different types.