Subtitle: why does this code work? It seems to allow comparison of NSNumber with NSString types via some sort of coercion. I'm trying to compare a selection from a UISegmentedControl with a previously stored value.
- (IBAction)minSegmentedControlChanged:(id)sender // MINIMUM value
{
UISegmentedControl *s1 = (UISegmentedControl *)sender;
NSMutableArray *pD = [[GameData gameData].curData valueForKey:#"persData"];
// Must make sure max >= min
NSNumber *currMax = [pD objectAtIndex:1];
NSLog(#"%#", [currMax class]); // __NSCFString ?!
int ss1 = s1.selectedSegmentIndex;
NSNumber *SS1 = [NSNumber numberWithInt:ss1 + 2];
if (SS1 >= currMax) SS1 = currMax;
NSLog(#"%#", SS1); // Answer is correct, appears to be an integer
NSLog(#"%#", [SS1 class]); // __NSCFString ?!
[pD replaceObjectAtIndex:0
withObject:SS1];
[[GameData gameData].curData setObject:pD
forKey:#"persData"];
NSLog(#"%#", [[GameData gameData].curData valueForKey:#"persData"]);
}
I am particularly asking about:
NSNumber *currMax = [pD objectAtIndex:1];
NSLog(#"%#", [currMax class]); // __NSCFString ?!
which seems to return a string for a number. [[GameData gameData].curData valueForKey:#"persData"]; is initialized as follows:
_persData = [[NSMutableArray alloc] initWithObjects:#"2", #"8", #"TWL", #"0", #"0", nil];
which is a string at element 1. So why can I ask it for an NSNumber, which reports that it is actually a __NSCFString on which I can do arithmetic comparisons on? I've only been at objective-c for a few months but this seems strange.
Okay, let's walk through this one step at a time.
First of all, all of the elements in _persData are strings. Period. NSString is a class cluster, so the concrete classes of the various instances you inquire about may look weird, but that's to support toll-free bridging and other magic that's not relevant to this discussion.
NSNumber *currMax = [pD objectAtIndex:1];
This line is incorrect. You might think there's some sort of coercion going on, but actually you're just assigning an NSString * to an NSNumber *. Which is wrong, and will explode in your face at the earliest convenience. It so happens that objectAtIndex: returns an id, which is stripped of type information, so the compiler is trusting you to store it in the right kind of pointer, but that's not enforced until you try to send a message to it.
if (SS1 >= currMax) SS1 = currMax;
This is an extremely wily comparison. SS1 is most certainly an NSNumber, but currMax is an NSString. But we're not comparing the values of those objects. To do that, we'd use the compare: method. Instead, we're comparing them as pointers, looking only at their addresses in memory. By some accident of implementation, SS1 seems to always reside at a higher address than currMax.
If all of the foregoing is true, then SS1 is always of type NSString after the above line is executed, which explains why this line:
NSLog(#"%#", [SS1 class]);
Always indicates that SS1 is a string.
You actually initialize the NSMutableArray with NSString, not number.
When you pull object out of NSMutableArray with objectAtIndex, the return type of the function is id, which it will blindly cast to NSNumber, while the actual object is NSString.
It seems that the SS1 >= currMax statement is comparing the addresses of the objects instead of their values.
You can test it out with this snippet of code:
NSNumber *a = [[NSNumber alloc] initWithUnsignedInt: 34];
NSNumber *b = [[NSNumber alloc] initWithUnsignedInt: 3];
NSNumber *c = [[NSNumber alloc] initWithUnsignedInt: 34];
NSNumber *d = [[NSNumber alloc] initWithUnsignedInt: 234];
NSLog(#"%p %p %p %p %d %d", a, b, c, d, a >= b, c >= d);
Related
_data = [NSMutableArray new];
NSNumber *value1 = [NSNumber numberWithFloat: 5.0f];
[_data setValue:value1 forKey:#"foothold"];
NSNumber *value2 = [_data valueForKey:#"foothold"];
NSLog(#"a foothold %f ",[value2 floatValue]);//error here
It's strange, but I don't see my error...
You have a few problems.
_data is mistakenly an NSMutableArray instead of an NSMutableDictionary.
Don't use setValue:forKey: and valueForKey: unless you mean to do KVC.
Use modern syntax (it's easier and it avoid issue #2).
Updated code:
_data = [NSMutableDictionary dictionary];
NSNumber *value1 = #5.0;
_data[#"foothold"] = value1;
NSNumber *value2 = _data[#"foothold"];
NSLog(#"a foothold %f ",[value2 floatValue]);
When you get an "unrecognized selector sent to instance" error, you'll be given the name of the method you're attempting to call (in this case floatValue) as well as the type of object you're calling it on (NSArray here).
So in this case, despite value2 being declared as an NSNumber, the value returned from [_data valueForKey:#"foothold"]; is an NSArray, which does not respond to the floatValue selector.
I'm surprised you weren't given other warnings. Given the syntax, it looks like you should be using an NSMutableDictionary rather than an array. In which case, try this:
NSMutableDictionary *_data = [NSMutableDictionary dictionary];
NSNumber *value1 = #5.0f;
_data[#"foothold"] = value1;
NSNumber *value2 = _data[#"foothold"];
NSLog(#"a foothold %f", [value2 floatValue]);
I have a UITextField, which I am trying to use a NSNumberFormatter with a NSNumberFormatterDecimalStyle to convert the text field into an NSNumber to initialise an object.
However when I do this, I can't get any sensible results out of the conversion. For the purpose of this example I have replaced the UITextField with a string, but I still get strange results. I am sure I am doing something daft, but any help would be appreciated.
//NSString * boardNumberText = self.txtBoardNumbs.text;
NSString * boardNumberText = #"42";
NSNumberFormatter * formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber * boardNumber = [formatter numberFromString:boardNumberText];
if ([boardNumber isEqual:nil]) {
NSLog(#"Number was null");
}
NSLog(#"Number was not null");
[self.board setNumber:boardNumber];
NSLog(boardNumberText);
NSLog([NSString stringWithFormat:#"%d", boardNumber]);
NSLog([NSString stringWithFormat:#"%d", self.board.number]);
The output that I get from the log file when I run this is:
2012-07-15 16:54:26.564 CoreDataDev[16123:fb03] Number was not null
2012-07-15 16:54:26.564 CoreDataDev[16123:fb03] 42
2012-07-15 16:54:26.565 CoreDataDev[16123:fb03] 135821152
2012-07-15 16:54:26.565 CoreDataDev[16123:fb03] 135820272
You can't just log NSNumber to the console using the integer format specifier, since NSNumber is a wrapper object. Also, NSLog takes a format string, so you don't need to use stringWithFormat:. Try this instead:
NSLog(#"%d", [boardNumber intValue]);
Using
NSLog(#"%d", boardNumber);
is wrong: it prints the memory address of the boardNumber pointer (the NSNumber instance itself). Use
NSLog(#"%d", [boardNumber intValue]);
instead.
P. s.: that
NSLog([NSString stringWithFormat:...]);
is unnecessary and ugly; NSLog has built-in format string handling, use it!
Someone use the following to initialize the NSstring
NSString *astring = [[NSString alloc] init];
I am wondering why not just use
NSString *atring = nil or NSString *astring = #""
There is no semantic difference between NSString *astring = [[NSString alloc] init]; and NSString *astring = #""; - but NSString *astring = nil; is completely different. The first two produce a reference to an immutable string value, the last indicates the absence of a value.
Whether the various ways of generating an zero-length string produce different objects is entirely an implementation detail. The code:
NSString *a = [[NSString alloc] init];
NSString *b = [NSString new];
NSString *c = #"";
NSString *d = [NSString stringWithString:#""];
NSLog(#"%p, %p, %p, %p, %p", a, b, c, d, #""); // %p = print the value of the reference itself
outputs (the exact values will vary):
0x7fff7100c190, 0x7fff7100c190, 0x1000028d0, 0x1000028d0, 0x1000028d0
showing only 2 zero-length string objects were created - one for #"" and one for alloc/init. As the strings are immutable such sharing is safe, but in general you should not rely on it and try to compare strings using reference comparison (==).
NSString *atring = nil
is different -- it's a nil pointer, not an empty string.
NSString *astring = #""
is almost the same, if you change it to something like
NSString* astring=[#"" retain];
It's one of the things that "don't matter"; he or she simply used one way. Probably for no particular reason at all.
NSString *atring = nil; is simply setting the pointer to nil and does nothing other than ensure that pointer is set to nil;
NSString *astring = #""; is a shorthand literal and is the equivalent of [NSString stringWithString:#""];
On another point I don't know why you would want to initialize a string to nothing if its not mutable since you won't be able to change it later without overriding it.
I'm literally going crazy whit these six rows of code.
NB: nome and prezzo are 2 textFields
NSString *itemName = (NSString *) [rowVals objectForKey:#"name"];
NSString *itemPrice = (NSString *) [rowVals objectForKey:#"price"];
nome.text = itemName;
nome.userInteractionEnabled = YES;
prezzo.text = itemPrice;
prezzo.userInteractionEnabled = YES;
Don't know why when itemPrice is copied in one of those label, the program go in SIGABRT.
Instead if I try to read the content with an NSLog(#"%#",itemPrice); it return the exact value, so it means that is a valid NSString.
The only solution I found is passing through a NSNumber:
NSNumber *itemPrice = (NSNumber *) [rowVals objectForKey:#"price"];
prezzo.text = [[NSString alloc] initWithFormat:#"%#", itemPrice];
There is another way to use directly the NSString?
Probably the value in the #"price" field is NSNumber, and not an NSString. The NSLog method will still provide a correct result, since %# is used for any NSObject subclass, not just NSString.
How about this:
NSString *itemPrice = [[rowVal objectForKey:#"price"] stringValue];
prezzo.text = itemPrice;
The problem might be the object type returned by [rowVals objectForKey:#"price"]. When you place the (NSString *) cast before the method call, you're telling the compiler what type of object is returned, but not actually converting it into an NSString. The line you use below does convert from NSNumber (or whatever other object) to a string: [[NSString alloc] initWithFormat:#"%#", itemPrice]
You might be storing NSNumber's object in NSDictionary instead of NSString.
There could be 2 ways: one would be to convert NSNumber to NSString while adding it to dictionary or the other way would be to convert NSNumber to NSString while assigning it to "itemName".
you may do the conversion for second option like:
NSString *itemPrice = [[rowVals objectForKey:#"price"]stringValue];
I am trying to store the outcome of a string from the array hypothesis into myArray
am I doing anything wrong?
myArray = [NSString stringWithFormat:#"%#", hypothesis];
Update
NSMutableArray *myArray
NSMutableArray *urArray
// this is where my words are converted into strings. e.g. if I said "HELLO"
- (void) pocketsphinxDidReceiveHypothesis:(NSString *)hypothesis{
if (x==1){
// I am trying to store "HELLO" into myArray for comparison later.
myArray = [NSString stringWithFormat:#"%#", hypothesis];
// this would print "HELLO"
self.textview.text = [NSString stringWithFormat:#"You said %#",hypothesis];
}
else {
urArray = [NSString stringWithFormat:#"%#", hypothesis];
}
}
this is basically it. after that I will compare myArray == urArray in an ifelse statement.
Try...
myArray = [NSArray arrayWithObject:[NSString stringWithFormat:#"%#", hypothesis]];
Yes, you are doing several things wrong.
you have a method -pocketsphinxDidReceiveHypothesis which you have defined as taking a single argument of type NSString*, the argument is called hypothesis.
In your question you say
I am trying to store the outcome of a
string from the array hypothesis
Which suggests that you know hypothesis is an array, but we can't verify this as you don't show that piece of code.
hypothesis must be a String or an Array. It cannot be both, you cannot be unsure.
If hypothesis is a String, ie. if you do something like:
NSString *input = #"Hello World";
[foo pocketsphinxDidReceiveHypothesis: input];
Then these two lines make no sense:-
NSMutableArray *myArray = [NSString stringWithFormat:#"%#", hypothesis];
NSMutableArray *urArray = [NSString stringWithFormat:#"%#", hypothesis];
Because, well look..
NSString *hypothesis = #"Hello World";
newString = [NSString stringWithFormat:#"%#", hypothesis];
This code does nothing, hypothesis and newString are identical, as you haven't even provided any arguments for the format. [NSString stringWithFormat:#"%#", hypothesis] is no different to just using hypothesis. So what you actually have is
NSString *hypothesis;
NSMutableArray *myArray = hypothesis;
NSMutableArray *urArray = hypothesis;
This is broken, you cant assign a String to an Array (well, i'm fairly certain you don't mean to anyhow). To use an Array you must use of of the several Array creation methods to give you, well, an Array. A String isn't an Array and can't pretend to be one.
Now, apologies if hypothesis isn't a String but is infact an Array (it would have helped if you had shown that piece of code). If it is an Array, ie. you do something like this..
NSArray *input = [NSArray arrayWithObject:#"Hello World"];
[foo pocketsphinxDidReceiveHypothesis: input];
Then your method definition is broken because you have defined it as taking a String argument
- (void)pocketsphinxDidReceiveHypothesis:(NSString *)hypothesis
When you need it to take an Array argument
- (void)pocketsphinxDidReceiveHypothesis:(NSArray *)hypothesis
Then the following two lines make no sense:-
NSMutableArray *myArray = [NSString stringWithFormat:#"%#", hypothesis];
NSMutableArray *urArray = [NSString stringWithFormat:#"%#", hypothesis];
[NSString stringWithFormat:#"%#", hypothesis] is exactly the same as [hypothesis description], which returns a String, so what you are effectively doing is:
NSArray *hypothesis;
NSString *hypothesisDescription = [hypothesis description];
NSMutableArray *myArray = hypothesisDescription;
NSMutableArray *urArray = hypothesisDescription;
So again, assigning a String to an Array variable - almost certainly not going to do what you want or need.
If hypothesis is a String and you meant to add it to an array, you must first make sure the array is initialized (ie it has to be a valid array). Something like NSMutableArray *myArray = [[NSMutableArray alloc] init] will do the trick. Then you can use one of NSMutableArray's methods to store your String, eg. [myArray addObject:hypothesis].
If hypothesis is an Array would you like to store the it in myArray as is or would you first like to transform it into a String?
Then you go on to say:-
I will compare myArray == urArray in
an ifelse statement
Given the confusion surrounding the preceding code it is not clear why you want to do this or what you hope to achieve. You have nowhere included a description of what this code is supposed to do. It is not clear whether you are aware that two Arrays that contain identical objects are not == (as the are two different arrays and have their own identity), but two pointers to the same Array are ==, eg:
NSArray *aSimpleArray = [NSArray arrayWithObjects:#"one", #"two", #"three", nil];
NSArray *foo = aSimpleArray;
BOOL result = (aSimpleArray==foo); // These are equal, result is true
NSArray *anotherSimpleArray = [NSArray arrayWithObjects:#"one", #"two", #"three", nil];
BOOL result = (aSimpleArray==anotherSimpleArray); // These are not equal, result is false
So unless you want to test if you have two pointers to the same Array (and not just two arrays with the same objects) == probably doesn't do what you want. Note there are methods to help compare Arrays, such as -isEqualToArray, so that:
NSArray *aSimpleArray1 = [NSArray arrayWithObjects:#"one", #"two", #"three", nil];
NSArray *aSimpleArray2 = [NSArray arrayWithObjects:#"one", #"two", #"three", nil];
BOOL areEqual1 = aSimpleArray1==aSimpleArray2; // FALSE
BOO areEqual2 = [aSimpleArray1 isEqualToArray:aSimpleArray2]; // TRUE
In general you have to be familiar with the interfaces of all the objects you are going to use
http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSArray_Class/NSArray.html
The Apple documentation is excellent but i recommend a good book. There are many posts about book recommendations on SO so i leave that to you. Otherwise Apple provides hundreds of simple sample projects that you should study.
Are you trying to add hypothesis to one of your arrays (i.e. your arrays are arrays of multiple hypotheses)? If so then you can use addObject:
[myArray addObject:hypothesis];
If the hypothesis string is actually supposed to represent the entire array then you'll need to explain how the elements are encoded into this string before we can help you.
I am trying to store the same text
that hypothesis is holding into
myArray.
In that case:
NSArray *myArray = nil;
…
myArray = [NSArray arrayWithObject:hypthesis];
But mainly I would suggest that you go through some good Objective-C tutorial, since you seem to be confusing many basic ideas and it’s hard to get somewhere without knowing the basics.