Comparing NSNumber value with defined int - objective-c

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.

Related

How does this Objective-C code work?

Code:
int main(int argc, const char * argv[]) {
id idObject = #"12345";
NSNumber *n = idObject;
NSLog(#"%#\n", [n description]);
return 0;
}
It prints "12345". How? I guess it's because Objective-C uses dynamic binding. Thus, decision which method to choose is made at run-time and this decision is based on the name of the method (selector) and the receiver object. Maybe the receiver object gets known due to "isa" pointer... ?
This works because:
All objects that inherit from NSObject have a description method.
Objective-C doesn't enforce types, so n is actually an NSString and not an NSNumber as you might suppose.
You are right.
The code works, because n refers to an object that understands the message description. (The object is a instance object of class NSString and these objects understand that message.)
The type of the object reference n (id, NSString*, NSNumber*, whatever) is without any meaning for the dispatching process.
At runtime you can collect many information about objects and its types. You cannot collect information about object references. (There is a single case, but this is not important.)
To add:
You're not actually typecasting by setting idObject to be referenced by NSNumber * n. The compiler doesn't know what type id should be, so it allows you to assign it to anything.
With your code snippet running you can see a bit more on how this is played out:
And then for comparison (creating an NSNumber from the string literal):

Errors in trying to cast to NSInteger * and NSMutableArray *

I'm a newbie in obj c. So I have a simple question.
I have a matrix of NSInteger values. It is called "curBoard". I want to update value at (x,y) coordinates with value "curStep". I have an arror "operand of type void where arithmetic..."
What am I doing wrong ?
[curBoard replaceObjectAtIndex:x withObject:(NSMutableArray *)[[curBoard objectAtIndex:x] replaceObjectAtIndex:y withObject:(NSInteger *)[NSNumber numberWithInt:curStep]]];
Update:
NSMutableArray *board;
board = [NSMutableArray new];
for(NSInteger i = 0; i<boardSize; i++) {
NSMutableArray *row = [NSMutableArray new];
for(NSInteger j = 0; j < boardSize; j++)
[row addObject:(NSInteger *)[NSNumber numberWithInt:0]];
[board addObject:row];
}
This withObject:(NSInteger *)[NSNumber numberWithInt:curStep]] part is what causing an issue. If you are storing as NSNumber objects, you should just use:
... withObject:[NSNumber numberWithInt:curStep]]
Edit:
From the code posted above, you should add it as:
[row addObject:[NSNumber numberWithInt:0]];
NSInteger is not of pointer type and you should use NSNumber itself to add to array.
Objective-C is basically just a bunch of object syntax strapped to C. The overall effect is something like strapping a jetpack to a horse: sometimes the two parts don't really work together very well. In this case, you're trying to go faster by telling the horse to giddy up, when you should really be opening up the throttle.
NSMutableArray is part of the jetpack—it's an Objective-C object and is only equipped to handle arrays of Objective-C objects. But NSInteger is part of the horse—it's a primitive C integer type, not a real object.*
I know NSInteger is capitalized like a class and has an NS prefix like a class, but it's really a creature of C. You can confirm this yourself—type Cmd-O in Xcode and type "NSInteger" into the Open Quickly dialog that pops up, and you'll be able to jump to its definition. In my current Mac project, that's typedef long NSInteger;; long is one of the primitive C types.
NSNumber exists to bridge the two. It's an object specifically designed to hold the C numeric types inside it. Since NSNumber is an object, NSMutableArray and other Objective-C things can deal with it.
But you can't just cast between NSNumber and NSInteger. NSNumber holds an NSInteger inside it, but that doesn't mean it's actually an NSInteger itself. If you put a sandwich in a plastic bag, you can't eat the bag.
Instead, you have to use NSNumber's +numberWithInteger: method to construct an NSNumber, and -integerValue to get the integer back out of it. (+numberWithInt: and -intValue will usually work, but they may behave differently with very large values, depending on whether your app is running on a 32-bit or 64-bit processor.) Actually, nowadays you can say [NSNumber numberWithInteger:foo] as #(foo) instead, which is a lot shorter.**
So when you add a number, you should be saying:
[row addObject:#(0)];
And when you later want that number back, you'll want to say something like:
n = [[row objectAtIndex:y] integerValue];
The -replaceObjectAtIndex:withObject: error is a different story. -replaceObjectAtIndex:withObject: doesn't return anything at all, so you can't use it as an argument. Luckily, you don't need to in this case. -replaceObjectAtIndex:withObject: doesn't create a new array; it alters the array that's already inside [curBoard objectAtIndex:x], so you don't need to do anything to curBoard. Instead, you can just write:
[[curBoard objectAtIndex:x] replaceObjectAtIndex:y withObject:#(curStep)];
* You actually used NSInteger *, which is slightly different. The * means "pointer to", so NSInteger * is a pointer to a primitive integer. This is sort of like NSNumber *, a pointer to an NSNumber object, so the compiler allows you to cast it.
Note that casting a pointer doesn't convert the data at the other end of the pointer; it just makes the compiler interpret the same data in a different way. If you actually tried to use the NSInteger * pointer to get data, you would either get garbage data or (for reasons too large to fit within this margin) crash.
In this case, though, once you've Jedi mind-tricked the compiler into thinking that value is a pointer to an NSInteger, you try to pass it to to -addObject:. -addObject: expects a pointer to an object, so the compiler balks at passing a pointer to an NSInteger instead.
** This syntax will work as long as you're using the iOS 6 SDK Xcode 4.4 or later, even if you actually run the app on an older iOS. It will also automatically use the right +numberWithWhatever: method for you, so you don't have to worry about picking the best one. When you're using a numeric literal like 0, the parentheses are optional, but they're required when you use a variable or constant. Of course, you can still do it the wordy way if you want, but there's little point nowadays.

Obj-c let me change the class of a variable, and its bad: How hunt it?

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).

Makes pointer from integer without a cast warning in singleton int

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;

objective-c question regarding NSString NSInteger and method calls

I like to create an instance of a custom class by passing it a value that upon init will help set it up. I've overridden init and actually changed it to:
- (id)initWithValue:(NSInteger *)initialValue;
i'd like to store this value in a property of the instantiated object and use the value to generate the filename of a UIImage that will be associated with it.
My question is how best to go about this in Cocoa/Obj-C. is an NSInteger the best parameter for my needs or would an NSString be better? Or is there another design pattern? I'm not familiar with all the creative methods available to help in this situation. Assume my class properties are:
#interface MyView : UIView {
UIImage *image;
NSInteger value;
If I stick with the NSInteger as a parameter I can easily assign value to it. but then would need to generate "image12.png" from that value (assume NSInteger = 12) to set up my UIImage. Something like "image"+initialValue+".png". Which I believe is verbosely expressed in Obj-C as:
NSString *myValue = #"image";
[myValue stringByAppendingString:[initialValue stringValue]]; //NSInteger may not respond to stringValue?!?
[myValue stringByAppendingString:#".png"]
This is where my understanding of Obj-C breaks down. Feels to me like this should be easier but I'm still confused.
Now if initialValue was an NSString with the value of #"12" maybe it is? then self.value = [initialValue integerValue];
and I could build image with multiple stringByAppendingString calls. Just seems tedious for something so simple in other languages.
What's best way to handle something like this?
Your init method looks fine, and if you're only dealing with integer values, you do want to use NSInteger as opposed to NSString or even NSNumber. The one thing you might want to change is the parameter you're passing should be (NSInteger), not (NSInteger *) - an NSInteger is not an object, but in Objective-C is a primitive type similar to int.
You can build the image name by using NSString's stringWithFormat: selector, as such:
NSInteger myInteger = 12;
NSString *imageString = [NSString stringWithFormat:#"image%d.png", myInteger];
Just to expand on what Tim mentioned, an NSInteger is actually just a #define. On 32-bit architectures, it's equivalent to an int and on 64-bit architectures it's a long.
If you need to treat an integer as an object (to put it in an NSDictionary, for instance), you can wrap it in an instance of NSNumber using numberWithInteger:.
Hope that helps!
One other thing to keep in mind though is that when you're dealing with paths NSString gives you stringByAppendingPathExtension: and stringByAppendingPathComponent:, both of which handle things like trailing slashes better than if you just use stringByAppendingString:.
If you describe a little more about what this object does and what kind of images it's loading I might be able to offer some advice if I can think of a better way of creating the filenames, instead of just passing a number around.