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;
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.
I'm trying to store a constant int value to compare against in a particular scenario. My define looks like this:
#define kApiSuccessCode 0
And I have a method which compares a statuscode (NSNumber) with this to give a BOOL result:
- (BOOL)isSuccess {
return [self.statusCode isEqualToNumber:[NSNumber numberWithInt:kApiSuccessCode]];
}
I have a synthesized NSNumber property like this:
#property (nonatomic, strong) NSNumber *statusCode;
The problem is I get a Execution was interrupted, reason: EXC_BAD_ACCESS error when running this code - I can't work out why? Is this a bad way to compare int values? Thanks
SOLVED:
Turns out I was making the basic mistake of trying to NSLog a BOOL value i.e NSLog(#"Does this work? %#", [response isSuccess]). So the code itself works - but THANK YOU to everyone for your suggestions for making this more efficient!
I don't know why the crash is occurring, but to answer your other question, yes, this is not a great way to compare int values.
There are valid reasons to store values in NSNumber instances, but in most cases it is overkill. If you do, in fact, have an NSNumber instance, just use intValue to get it's integer value and compare it to a primitive instead of creating a whole new instance.
If you look at Foundation classes, you'll see most of the time, they rely on NSInteger primitive types instead of NSNumber instances. For example, the NSURLResponse class uses an NSInteger to return the HTTP status code.
I would enable Zombies as instructed in this post.
It will then tell you if you are sending the message to a deallocated instance of the variable.
Once you figure this out, I would then suggest this instead:
- (BOOL)isSuccess {
return [self.statusCode intValue] == kApiSuccessCode;
}
Advantage and disadvantages of #define vs. constants?
As mentioned by others, #define doesn't have a type associated with it
=> kApiSuccessCode is not an integer, the pre-compiler just replace it with 0 before you program is compiled.
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).
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.
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.