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.
Related
In some sample code, an NSNumber is added to a mutable array:
// In the .h, as an instance variable:
NSMutableArray *sequence;
// In the .m file:
sequence = [[NSMutableArray alloc] initWithCapacity:100];
[sequence addObject:[NSNumber numberWithInt:123]];
Then later on, when the integer is needed, it uses:
[(NSNumber *)[sequence objectAtIndex:aCounter] intValue]
I just wonder why the cast (NSNumber *) is needed? Because the program runs fine without it, too. Is it just a good practice? If so, what can it prevent from happening? If there is a bug so that one element is not an NSNumber *, then casting it can create strange behavior too.
Casting only makes the compiler believe that the object (which is returned as of type id, i. e. generic object type with no other information!) is actually an NSNumber, so that it can identify correclty its intValue etc. methods. It doesn't make things differ at runtime. If the object is not an NSNumber, then it will crash at runtime, with or without the casting.
It is fine to do it without the casting, the casting just makes it explicit that you treat it as an NSNumber, if you have a bug and this is not an NSNumber (or more precisly, don't respond to intValue) you'll get some odd behavior anyway.
//In objective-C any object can send message to any other object.
//SO here both statmenst are perfectly valid ,but
[(NSNumber *)[sequence objectAtIndex:aCounter] removeFromSuperview]; //This throws warning and lets u know removeFromSuperview shpuld'nt be called
[[sequence objectAtIndex:aCounter] removeFromSuperview];//here u wont get any warnig
I just wonder why the cast (NSNumber *) is needed?
It's not needed if the signature of the selector that's actually called at runtime is visible to the translation, and all selector signatures visible to the translation match for the selector that's called.
You're probably thinking "What? That's complicated! That's also error prone, especially as my programs evolve!"
If multiple selector signatures for the same selector are visible and you message id, then you should expect undefined behavior because objc collections aren't typed and the compiler may not match the correct selector (if your warning level is high and your includes are all correct, you can see a warning about this).
The simple way to avoid this is to reintroduce the correct type by assignment:
NSNumber * n = [array objectAtIndex:i];
int a = [n intValue];
or by casting:
int a = [(NSNumber*)[array objectAtIndex:i] intValue];
so the compiler can match the selector appropriately for the type, and also warn you when the object may not respond to a given selector, or if the parameters or return types do not match, or if the interface of the type you cast it to is not visible in the translation -- after all, you should have an idea of what the collection contains.
Introducing that type safety properly is a very good practice.
The cast is just needed to stop the compiler from complaining that it's not sure you know what you're doing.
One of the things that the compiler does for you as it compiles is to check whether the interfaces for the recievers of messages say that they respond to the messages you're sending (intValue in this case). The interface of NSNumber does indeed say that it responds to intValue, but the return type of objectAtIndex: is id, which is a generic pointer. The compiler has no way to know what the type of the object at the other end of that pointer is -- that won't be known until runtime.
The cast tells the compiler that you do indeed know the type and that it doesn't need to warn you (or, in some cases under ARC, give an error) about the fact that it's not sure if the receiver of the message responds.
Note that if you changed the class of the cast to something that didn't respond to intValue (such as NSDate), the compiler would gripe at you, but if the object really was still an NSNumber, the message would still succeed at runtime. Casting can't change the type of the object; it is simply an annotation for the compiler.*
*In some cases, it can also increase the readability of your code, too.
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.
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!
I have next situation:
Method:
-(void) myMethod:(id)inValue
{
long a = [inValue longValue];
}
Compiler shows me a warning that -longValue - is multiplied:
multiple methods named '-longValue' found
What can I do to resolve this warning without change method name?
Thank!
Strongly type your method's argument to tell the compiler which variant of the -longValue message you want to use, e.g.:
-(void) myMethod:(NSNumber *)inValue
{
long a = [inValue longValue];
}
If you want to accept multiple types that respond to -longValue (say, NSNumber and NSString) then you'll have to go back to using id and you'll see the warning. You see, something in your (yes, your, not Apple's) class hierarchy has bunged up and used a different signature for -longValue, so the compiler has no way of knowing which one you want to use. id tells it "this is an object" but it provides no explicit information that the compiler can use to resolve its conundrum.
Is there a particular reason why you're passing an id instead of a strongly-typed object?
I'm understood where from this warning - it completely right. If I were compiler developer - I were make possible something like this:
id a = [[[inValue class] alloc]] init];
for type cast; and after this may be:
a = inValue
[a longValue];
for resolve this warning.
But I'm not.)
And I'm only study programming for Mac. So i just asking - it possible or not.
Just like back in the pre-generics Java days, I write code like this:
UIView *ball = [self.balls objectAtIndex:i];
which causes a compiler warning. I know that correct way to resolve it -- assuming it doesn't blow up at runtime -- is to use
UIView *ball = (UIView *)[self.balls objectAtIndex:i];
but recently I've been favoring
UIView *ball = (id)[self.balls objectAtIndex:i];
Is there ANY reason not to do this? Does it change anything at runtime? It's easier to type and read, I think.
Either you've defined a custom type that responds to objectAtIndex: rather than using a built-in array or there is something wrong with your setup. NSArray's objectAtIndex: is defined to return type 'id', so casting the result to type 'id' has no effect. Furthermore, objects of any type can be assigned to id and objects of type id can be assigned to any other type without generating a compiler warning.
In any event, pointer casting has no effect at runtime. Objective-C does not modify the way that pointers work in C other than supplying a few additional implicit conversions.