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.
Related
Consider this code:
NSMutableArray *array = [[NSMutableArray alloc]init];
for(int i = 0; i < 5 ; i++)
[array addObject:[[NSDictionary alloc]init]];
NSString *poisonedString = [array objectAtIndex:0];
In this above snippet, I have an array and I am inserting some dictionary objects into this array. While all this is fine and dandy, when I get the dictionary object from the array and put it into an string object, it is not causing any cast exceptions! The program runs smoothly and then crashes only when we do dictionary operations on the poisonedString! Why is this so? Why doesn't the Objective-C runtime see the type mismatch and raise an exception at the assignment, when the dictionary object is put into poisonedString?
Objective-C is not type-checked at runtime at all unless those checks are explicitly added. What is done at runtime is method lookup in response to a message, and in this the type of the target is not considered only whether the target has an matching method.
At compile time the Clang compiler does as much static type-checking of the Objective-C additions to C as it can to provide the programmer with warnings when types are incorrect. However the programmer can always bypass those warnings with little difficulty if they really wish to, but it is generally ill-advised to do so.
Whenever the type of something is "lost", e.g. when it is put into a collection which allows for any type of object and later extracted, then the compiler cannot provide any meaningful help. It is up to the programmer to add code to check the actual runtime type and then cast the value to the determined type. While the cast itself does nothing at runtime at compile it informs the compiler of the determined type and that allows it to do better type checking and produce better warnings.
You test the type of something with the method isKindOfClass, so the outline template is:
id someVar = ...
if ( [someVar isKindOfClass:[SpecificType class]] )
{
SpecificType *typeKnownVar = (SpecificType *)someVar;
// now use typeKnownVar and compiler will do static checking where possible
}
else
{
// deal with someVar not being of SpecificType
}
HTH
In a nutshell, NSArray supports inserting any type of object into it. If you look at the documentation you can see a generic (id) object is returned when using objectAtIndex:
- (id)objectAtIndex:(NSUInteger)index
With that in mind you can't expect the compiler to know ahead of time what type your object is when you get it from the array.
Because first of all you are not casting :) I can't see any cast, and second of all, welcome to Objective-C, this is what they call a Dynamic Language, and you can read more here
https://developer.apple.com/library/mac/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html
Now back to your code, the arrays are not generic so you can add whatever items of whatever classes you want in the same array and when you get the item, you don't need to cast it, the compiler trusts you :D now if you try to send a message (or as you named it do some operations), it'll throw a runtime exception, so simply if you are in the situation where you don't know if it's an NSString or an NSDictionary, just use the below code to check the type of the object.
if ([array[0] isKindOfClass:[NSDictionary class]]) {
// Do whatever you want
}
else if ([array[0] isKindOfClass:[NSString class]]) {
// Also do whatever you want
}
in NSArray class reference the documentation present that the return of ObjectAtIndex is an id
objectAtIndex:
Returns the object located at the specified index.
- (id)objectAtIndex:(NSUInteger)index
It is not possible to the compiler to know the type of object when your pointer is an id. It's normal that dump at executing time and not at compiling time.
Well, in obj-c have the ability of change the class of a declared var. So if I declare myVar as a NSString, is possible to get back later a NSNUmber.
I have this problem now, but I can't find where in my code is the identity swap... exist a way to find it? For example is possible to set a breakpoint where [myVar class] == [NSString class] and when change know it?
You may be confused about the static type of a pointer, and the actual type of the object it points to. Consider this code:
NSString *test = #"test";
NSNumber *notReallyANumber = (NSNumber *)test;
This is valid code, but it didn't "transform" test into an NSNumber. It's still a string, just with an incorrect type on the pointer.
Basically, no, you don't have the ability to change the class of a variable (you do, but it's deep deep magic and almost never occurs).
Objective-C, xCode for iOS
In a class, I want to assign a singleton integer's value. Right now I have:
[ExGlobal sharedMySingleton].tetraCountEx = tetraCount;
I've got this warning before, and have been able to resolve it, but this seems like I would have to do something different by letting the compiler know that tetraCountEx is an integer. I just don't know how.
That error is a result of trying to store a number as a pointer. With out you posting any code as to how tetraCountEX is declared I can only guess what your problem is.
On reason may be that tetraCountEx is defined as an NSNumber and if that is the case use
[ExGlobal sharedMySingleton].tetraCountEx = [NSNumber numberWithInt:tetraCount];
//or numberWithInteger: or the appropriate type
And the other reason may be accidentally declaring tetraCountEx as a pointer
//Remove the * if this is the case
#property(nonatomic, assign) int *tetraCountEx;
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.
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.