discussing functionality of id's stringValue function? - objective-c

I got a NSMutableArray object with int values
and I can get a certain value via :
int *v0=[[[arrayObj objectAtIndex:0] intValue];
there is no problem.
But
I got a NSMutableArray object with NSString values
and I cannot get a certain value via :
NSString *v0=[[[arrayObj objectAtIndex:0] stringValue];
//raises error
I want to learn and understand exactly what stringValue for... and why this error occurs ?
NSString *v0=[arrayObj objectAtIndex:0];
works as expected.I asusme its some kind of pointer with null terminated so it can leech value.
Im not sure this line is also unicode/encoded string safe code.
in conclusion:
want to know the purpose of stringValue with some lines o code snippets

I got a NSMutableArray object with int values
That's not possible, Cocoa arrays always contain objects. You probably have an array of NSNumber objects that wrap the integers, like:
NSArray *arrayOfNumbers = #[#1, #2, #3];
NSNumber objects have an intValue method, so this works:
int value = [arrayOfNumbers[0] intValue];
On the other hand when you have an array of strings ...
NSArray *arrayOfStrings = #[#"1", #"2", #"3"];
... you want to access individual elements directly, without converting the string object to something else:
NSString *element = arrayOfStrings[0];
NSString objects do not understand the stringValue method:
[arrayOfStrings[0] stringValue]; // crash: does not recognize selector
Back at the beginning, our NSNumber objects from the first array do understand stringValue. You can use it to convert the number to a string:
NSString *intString = [arrayOfNumbers[0] stringValue];
To make the confusion perfect, NSString also understand the intValue message:
int value = [arrayOfStrings[0] intValue];
Here intValue means to try to convert the string to a plain C int value.

The error you will be getting (but failing to post with your question) will be Unknown selector sent to instance and this is because NSString doesn't have a stringValue method.
The approach you suggest is correct:
NSString *v0 = [arrayObj objectAtIndex:0];
EDIT (prompted by #Answerbot's answer):
The reason you are confused is that [NSString intValue] is used to convert the string value to an integer, as long as the string represents an integer (i.e. #"123"). However you don't need this for string as the object is already a string. It's therefore not provided.

Related

objective-c - Why can I assign values to pointers?

I understand pointers work with addresses and not the data itself. This is why I need to use the address-of (&) operator below as I need to assign the address of num to the pointer and not the actual value of num (40).
int num = 40;
int *numPtr = #
Therefore i'm confused as to why I can do this.
NSString *str = #"hello";
I've created a pointer str but instead of giving it an address i'm able to assign it some data, a literal string.
I thought pointers could only hold memory addresses so why am I able to directly assign it some data?
For someone trying to get their head around pointers and objects this is very confusing.
No you are not assigning a literal string to it, # makes a NSString object with the string value hello.
In most C languages strings are just an array of char, where char is a primitive type like int like in your example.
There is a reason you put an # before string literals (when you want an NSString and not a C string) in objective-c
#"String" is basically equivalent to [NSString stringWithCString:"string"] which returns a pointer to an NSString object containing the value "string"
It is the same way 1 is a c type integer, but #1 is a NSNumber representing the value of 1. If you see an # it means "this is shorthand for creating an object". (#[] for NSArrays, #{} for NSDictionarys, #(), #123, #YES, #NO for NSNumbers, and #"" for NSString)
C does not have strings. Usually char arrays are used to represent them.
NSString *str = #"hello";
can be thought of as short hand (literal) for:
char charArray[] = "hello";
NSString *str = [[NSString alloc] initWithBytes:charArray length:sizeof(charArray) encoding:NSUTF8StringEncoding]; // disregard character encoding for this example
or
unichar bla[] = {'h', 'e', 'l', 'l', 'o'};
str = [[NSString alloc] initWithCharacters:bla length:sizeof(bla)];
So an object is created and thus you need a pointer.

Having trouble taking an index of an array and making it an NSString

I get an array from a JSON and I parse it into an NSMutableArray (this part is correct and working). I now want to take that array and print the first object to a Label. Here is my code:
NSDictionary *title = [[dictionary objectForKey:#"title"] objectAtIndex:2];
arrayLabel = [title objectForKey:#"label"];
NSLog(#"arrayLabel = %#", arrayLabel); // Returns correct
//Here is where I need help
string = [arrayLabel objectAtIndex:1]; //I do not get the first label (App crashes)
NSLog(#"string = %#", string);
other things that I have already tried are as follows:
string = [NSString stringWithFormat:#"%#", [arrayImage objectAtIndex:1]];
and
string = [[NSString alloc] initWithFormat:#"%#", [arrayImage objectAtIndex:1]];
Any help is greatly appriciated!
EDIT: The app does not return a single value and crashes.
Your code doesn't match the structure of your JSON. In your comment on the deleted answer, you said you got an exception when sending objectAtIndex: to an NSString. In your case, arrayLabel isn't an array when you think it is.
If your JSON has an object, your code needs to treat it as an NSDictionary. Likewise for arrays and NSArray and strings and NSString.
In addition to whatever else was going on, you repeatedly refer to "first" but use the index 1. In most C-based programming languages (and others, as well) the convention is that indexes into arrays are 0-based. So, use index 0 to get the first element.

What's the real type? NSString or NSNumber

I have a NSDictionary that contains data converted from json data, like {"message_id":21}.
then I use NSNumber *message_id = [dictionary valueForKey:#"message_id"] to get the data.
but when I use this message_id,
Message *message = [NSEntityDescription ....
message.messageId = message_id;
I got the runtime error, assigning _NSCFString to NSNumber,
so I have to use NSNumberFormatter to do the conversion.
NSString *messageId = [dictionary valueForKey:#"message_id"];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterNoStyle];
message.messageId = [f numberFromString:messageId];
this code works.
but when I was debugging, I saw message_id of
NSNumber *message_id = [dictionary valueForKey:#"message_id"]
has a valid value, 21.
Can anyone see the problem here?
You are trying to save a NSString to a NSNumber. If you want it as an NSNumber you can do:
NSNumber *message_id = [NSNumber numberWithInt:[[dictionary valueForKey:#"message_id"] intValue]];
This should solve your problem.
What library are you using to do the conversion? {"message_id":21} means that an NSNumber with a value of 21 should be returned as an NSNumber, {"message_id":"21"} should return it as an NSString.
Using a number formatter is total overkill. Use the method "integerValue" which works just fine both with NSString* and with NSNumber* - you will get the integer 21, whether the object is NSString or NSNumber. The formatter code will obviously run into trouble if your object is an NSNumber and not an NSString.
So: message.messageId = [[NSNumber numberWithInteger:[messageId integerValue]];
I'd probably add a category to NSDictionary
(NSNumber*)nsIntegerNumberForKey:(NSString*)key
which handles the situations where the key is not present, or where the value is a null value or a dictionary or array, so you can use it everywhere you need an NSNumber with an integer value from a JSON document and have error checking everywhere.
Read here SAVING JSON TO CORE DATA and JSON official page
The JSON standard is quite clear about how to distinguish strings from
numbers– basically, strings are surrounded by quotes and numbers are
not. JSON web services however, are not always good about following this requirement. And even when they are, they are not always consistent from one record to another.
So if you have receive NSNumber where NSString is preferred, you must inspect and fix yourself

How to append NSString wiht number?

I'm new in Cocoa.
I have NSString - (e.g) MUSIC . I want to add some new NSString in Array,
And want to check something like this
if MUSIC already contained in Array, add Music_1 , after Music_2 and so on.
So I need to be able read that integer from NSString, and append it +1 .
Thanks
Use
NSString *newString = [myString stringByAppendingString:[NSString stringWithFormat:#"_%i", myInteger]];
if myString is "music", newString will be "music_1" or whatever myInteger is.
EDIT: I seem to have gotten the opposite meaning from the other answer provided. Can you maybe clarify what it is you are asking exactly?
Check it out:
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"123", #"qqq", nil];
NSString *myString = #"MUSIC";
NSInteger counter = 0;
if ([array containsObject:myString]){
NSString *newString = [NSString stringWithFormat:#"%#_%d", myString, ++counter];
[array addObject:newString];
}
else
[array addObject:myString];
For checking duplicate element in Array you can use -containsObject: method.
[myArray containsObject:myobject];
If you have very big array keep an NSMutableSet alongside the array.Check the set for the existence of the item before adding to the array. If it's already in the set, don't add it. If not, add it to both.
If you want unique objects and don't care about insertion order, then don't use the array at all, just use the Set. NSMutableSet is a more efficient container.
For reading integer from NSString you can use intValue method.
[myString intValue];
For appending string with number you can use - (NSString *)stringByAppendingString:(NSString *)aString or - (NSString *)stringByAppendingFormat:(NSString *)format ... method.
Here's how you convert a string to an int
NSString *myStringContainingInt = #"5";
int myInt = [myStringContainingInt intValue];
myInt += 1;
// So on...

Why does fast enumeration not skip the NSNumbers when I specify NSStrings?

I thought that I knew how to use fast enumeration, but there is something I don't understand about it. If I create three NSString objects and three NSNumber objects and put them in an NSMutableArray:
NSString *str1 = #"str1";
NSString *str2 = #"str2";
NSString *str3 = #"str3";
NSNumber *nb1 = [NSNumber numberWithInt:1];
NSNumber *nb2 = [NSNumber numberWithInt:2];
NSNumber *nb3 = [NSNumber numberWithInt:3];
NSArray *array = [[NSArray alloc] initWithObjects:str1, str2, str3, nb1, nb2, nb3, nil];
then I make do fast enumeration on all NSString objects, like this:
for (NSString *str in array) {
NSLog(#"str : %#", str);
}
In the console, I get this result :
2011-08-02 13:53:12.873 FastEnumeration[14172:b603] str : str1
2011-08-02 13:53:12.874 FastEnumeration[14172:b603] str : str2
2011-08-02 13:53:12.875 FastEnumeration[14172:b603] str : str3
2011-08-02 13:53:12.875 FastEnumeration[14172:b603] str : 1
2011-08-02 13:53:12.876 FastEnumeration[14172:b603] str : 2
2011-08-02 13:53:12.876 FastEnumeration[14172:b603] str : 3
I logged only the NSStrings, but I get a line for every object in the array, even the NSNumbers and I don't understand why. Does fast enumeration always use every object contained in an array?
When you write a forin loop like that, it casts every object in the array as an NSString, then prints them out as requested.
If you want only the NSStrings, you would need to write something like this:
for (id obj in array) {
if ([obj isKindOfClass:[NSString class]]) {
NSLog(#"str: %#", obj);
}
}
The for all loop doesn't know the difference between NSStrings and Integers -- it will simply go through the entire array, cast each as an NSString, and print them out as you asked.
I'm pretty sure that fast enumeration returns all objects in the array- all that you're doing in for (NSString *str in array) is typecasting str to an NSString. In the body of the loop you need to check the class of the returned object to make sure that it is an NSString.
for(NSString *str in array)
{
if([str isKindOfClass:[NSString class]])
NSLog(#"str : %#", str);
}
Objective-C is dynamically typed, meaning that at runtime (when the loop actually runs), objects are all effectively one type (id) with different classes. The language allows optional compile-time static typing, but all that does is check whether the messages you're sending are valid for the type you've marked. It doesn't actually change the behavior of your program. If you cast an object to be a different type than it actually is, all you're doing is lying to the compiler and defeating its type-checker.
Every object that descends from NSObject implements the method - (NSString)description, %# in Objective-C formate string will take the corresponding argument for the %# and call its description method, Most subclasses of NSObject will implement there own version of - (NSString)description. The same thing happens when you type
> po anObject
in the debugger.
for (NSString *str in array) {
is a way to enumerate through all the elements in array.
You expectative that by specifying NSString you get only the objects of that type is not correct. Rather, all the objects pointers are cast to that type (NSString*).
Have a look at Fast Enumeration in The Objective-C Programming Language guide.
I don't understand where is the unexpected behavior, using the enhanced for loop in an NSMutableArray will just iterate thru every single object in the array which in your case is 6, the result is correct and expected.
The numbers will just get casted to Strings.
in fast enumeration no typecasting,just assigning the pointer into new object