Seems like null NSString has length 6? - objective-c

I have a person object of class Person with property is address, age. When I check properties, I print address on screen with:
NSLog(#"Add: %#, length: %i", person.address, [person.address length]);
The result is: Add: nil, length: 6
Can you explain for me why the string is null but its length shows as 6?
Thanks.

You are probably getting the string value of "(null)" in the field somehow, which is 6 characters long.

What type is address? NSString I assume? use %d or %lu for length (and cast to unsigned long if using %lu) instead of %i - that should give correct result.
Length gives a NSUInteger as a result, correct string format specifier would hence be %lu although %d should also work (thinking of it, I always use %d). I never as %i...
NSLog(#"%lu", (unsigned long)string.length)
Look at the format specifies:
https://developer.apple.com/library/mac/ipad/#documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html

The answer you're probably looking for is: you're using the wrong string format specifier. I'm not sure what %i is; %d is for integers and %u for unsigned, so you need:
NSLog(#"Add: %#, lenght: %u", person.address, [person.address length]);
But... the length is possibly not returning what you think it is.
If person.address is nil, then sending the message length to it won't return the length (what is the length of "nothing"?). As it happens, the Objective C runtime will return zero so your example "works" but is arguably not correct.

Related

What does stringWithFormat:#"%1$#%2$d" do?

stack = [NSString stringWithFormat:#"%1$#%2$d", stack, number];
I'm following a Xcode calculator tutorial, and I'm not too sure what does the %1$#%2$d represents. Please guide me.
%# says the argument is an Objective-C object and it sends one of the description selectors to get the string that will be inserted into the final string.
%1$# says the same thing, but specifies the first argument.
%d is a signed, 32-bit integer.
%2$d specifies the second argument is a signed, 32-bit integer.
This format is used to explicitly select which argument should be replaced in the string so 1$ is for the first argument, 2$ for the 2nd etc...
The '#' is for ObjC objects (which in general displayes an object's description), and 'd' is for integers
In this case it could also be written simply as :
stack = [NSString stringWithFormat:#"%#%d", stack, number];
I assume you know what %# and %d mean. By default, the first specifier(such as %#) will be replaced by the value of the first argument in argument list, and so on. However, n$ gives you the power to specify the argument at which position you want to use its value to replace the specifier containing n$.
In fact, a simple example is much clearer:
NSString *aString = #"ultimate answer";
int anInteger = 42;
NSLog(#"The %# is %d.", aString, anInteger); // The ultimate answer is 42.
NSLog(#"The %1$# is %2$d.", aString, anInteger); // The ultimate answer is 42.
NSLog(#"%2$d is the %1$#.", aString, anInteger); // 42 is the ultimate answer.
[NSString stringWithFormat:#"%1$#%2$d", stack, number];
breaks down logically to mean you want a string (you get that from string with format), displaying two items (you can see that from the items after the string, and the number of % symbols in the format.
%1$#%2$d is for two items, you can break them by the %, %1 and %2 mean first and second.
%1$# - # represents an object that wil be translated to a string
%2$d - d represents a decimal.

"unsigned int" prints as a negative number?

I am taking an integer, in this case 192, and left shifting it 24 spaces. The leading 1 is causing it to become negative, it seems.
unsigned int i = 192;
unsigned int newnumber = i << 24;
NSLog(#"newnumber is %d",newnumber);
I am expecting 3,221,225,472 but I get -1,073,741,824 (commas added for clarity)
An unsigned integer shouldn't be negative right?
Because you reinterpret it in NSLog as a signed integer. You should use %u to see an unsigned value.
There is no way for a function with variable number of arguments to know with certainty the type of the value that you pass. That is why NSLog relies on the format string to learn how many parameters you passed, and what their types are. If you pass a type that does not match the corresponding format specifier, NSLog will trust the specifier and interpret your data according to it. Modern compilers may even warn you about it.
You wan to do NSLog(#"newnumber is %u",newnumber);
%d converts it back to a signed int.
%d means "signed integer"; use %u for "unsigned integer".

Working with big numbers in Objective-C?

I need to convert values like 1393443048683555715 to HEX. But, first of all, i cann't display it as decimal using NSLog(), for example.
Ok, it works:
NSLog(#"%qu", 1393443048683555706);
But what about converting to HEX. What type i have to use to store this big value?
NSLog([NSString stringWithFormat: #"%x", 1393443048683555706]);
// result eb854b7a. It's incorrect result!
but i forgot to say that this big number represented as string #"1393443048683555706" (not int)
You can use %qi and %qu format specifiers with NSLog to display 64-bit integers. Your constant appears to fit in 64-bit signed number, with the limits of:
[−9223372036854775808 to 9223372036854775807]
The "x" format specifier is for 32-bit numbers; you need to use either "qx" or "qX" (depending on whether you want the letter values to be uppercase or not). These are the formatters for unsigned long long values, see:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265-SW1
Next, you should not pass a string as you have done above directly to NSLog - this can cause a crash.
NSLog(string); // bad!!
NSLog(#"%#", string); // good
So if your value comes as a string, you'll want to do this:
NSString *longNumber = #"1393443048683555706";
NSLog(#"%qx", [longNumber longLongValue]);
If the string value can't be coerced to a number, longLongValue will return 0. I'll leave it to you do handle the error (and bounds) checking - see NSString for details.
If you want to save the hex value as a string, do this:
NSString *hexRepresentation = [NSString stringWithFormat:#"%qx", [longNumber longLongValue]];
Again, best to take care for error handling.

Objective-C Converting an integer to a hex value

I've got a dictionary initialized like so...
keyDictionary = [[NSDictionary dictionaryWithObjects:values forKeys:keys]retain];
where keys is an NSArray of the alphabet and other characters and values is an NSArray of unsigned chars, which are the USB hex keycodes for those characters.
The USB key codes are hex values that range from 0x04 to 0xE7. I'm trying to create a map between these two depending on what key is pressed on the keyboard.
The values array is created like so...
NSArray *values = [NSArray arrayWithObjects:
[NSNumber numberWithUnsignedChar:0x04]/*A*/,
[NSNumber numberWithUnsignedChar:0x05]/*B*/, /*ETC*/];
So ideally when I run this code...
where character == #"I"
- (uint8) getUSBCode:(NSString *)character
{
NSNumber *val = [keyDictionary objectForKey:character];
return (uint8)[val unsignedCharValue];
}
I would expect to get back 0x0C, but I'm getting 12 back as an int (which after I thought about it, makes sense). I need the hex value preserved. I do NOT need a string value. I need a straight conversion to the hex value or a better way to store
uint8 is just a typedef unsigned char.
EDIT I was not clear when I posted this earlier. Here's what I need.
I need the hex value of these codes because they are being sent over the internal company network. In addition, the pressed key's value is being converted from big endian (or little, it's escaping me right now which one it is) to the other, then being transmitted over an internal network. I understand that these values are stored in binary, but I need to transmit them in hex.
Also, I stated I was getting 12 back from the function. I was reading 12 from the debugger, not actually getting the value. That might be why I was getting confused.
12 (in base 10) is 0x0c.
If you want to print it out in hex, use the %x format specifier e.g.
NSLog(#"Hex value of char is 0x%02x", (unsigned int) c);
If you want to see it in hex in the debugger (assuming Xcode 3.2.x) right click on the variable and select hexadecimal as the format.
You know that an int is stored in binary (i.e. the 'hex' value is always and never preserved), so I'm interpreting your question as pertaining to printing to the screen.
You should be able to use a format specifier for that -- something like %0x.
The value that's returned from your -getUSBCode: method isn't two decimal digits, it's one eight-bit byte. Both "12" and "0x0C" are strings that represent that byte's value, so saying you want "0x0C" but don't want a string is a contradiction.

Basic problems (type inference or something else?) in Objective-C/Cocoa

Apologies for how basic these questions are to some. Just started learning Cocoa, working through Hillegass' book, and am trying to write my first program (a GUI Cocoa app that counts the number of characters in a string).
I tried this:
NSString *string = [textField stringValue];
NSUInteger *stringLength = [string length];
NSString *countString = (#"There are %u characters",stringLength);
[label setStringValue:countString];
But I'm getting errors like:
Incompatible pointer conversion initializing 'NSUInteger' (aka 'unsigned long'), expected 'NSUInteger *'[-pedantic]
for the first line, and this for the second line:
Incompatible pointer types initializing 'NSUInteger *', expected 'NSString *' [-pedantic]
I did try this first, but it didn't work either:
[label setStringValue:[NSString stringWithFormat:#"There are %u characters",[[textField stringValue] length]]]
On a similar note, I've only written in easy scripting languages before now, and I'm not sure when I should be allocing/initing objects and when I shouldn't.
For example, when is it okay to do this:
NSString *myString = #"foo";
or
int *length = 5;
instead of this:
NSString *myString = [[NSString alloc] initWithString:"foo"];
And which ones should I be putting into the header files?
I did check Apple's documentation, and CocoaDev, and the book I'm working for but without luck. Maybe I'm looking in the wrong places..
Thanks to anyone who takes the time to reply this: it's appreciated, and thanks for being patient with a beginner. We all start somewhere.
EDIT
Okay, I tried the following again:
[label setStringValue:[NSString stringWithFormat:#"There are %u characters",[[textField stringValue] length]]]
And it actually worked this time. Not sure why it didn't the first time, though I think I might have typed %d instead of %u by mistake.
However I still don't understand why the code I posted at the top of my original post doesn't work, and I have no idea what the errors mean, and I'd very much like to know because it seems like I'm missing something important there.
The first problem is the fact that -[NSString length] returns an NSUInteger, not an NSUInteger *. The first is an integer; the second is a pointer to an integer (more on that in a second). The second problem is that this line:
NSString *countString = (#"There are %u characters",stringLength);
Doesn't do what you expect. I think you want to format a string, but what this is actually doing is executing the expression #"There are %u characters", then stringLength, and returning the value of the second expression. What you want is this:
NSString *countString = [NSString stringWithFormat:#"There are %u characters", stringLength];
Which you said didn't work, but I'm not sure why.
For example, when is it okay to do this ... instead of this:
It's "okay" when you're setting a variable of that type to return value of the same type. In C (and Objective-C is just some stuff on top of C), there are values and then there are pointers to values. Pointers to values are actually memory addresses that point to a value in memory. For example, this is a value of type int:
int x = 5;
And this is a pointer to that value:
int *xptr = &x;
x has the value 5. xptr has some memory address as its value. *xptr (the dereferenced value of xptr) has a value of 5, since xptr points to a value of 5 in memory.
This is not something you have to worry about much in Cocoa. The API docs will tell you the return values of methods and functions, and Objective-C objects are always pointers. There are a few types to watch out for, though: NSRect, NSPoint, and some other values are just C structs (not Objective-C objects) and thus sometimes you deal with the values themselves, and not pointers. And things like NSUInteger are generally not pointers, either (but sometimes they are).
(For some reason I can't edit my original post.)
Okay, I tried the following again:
[label setStringValue:[NSString stringWithFormat:#"There are %u characters",[[textField stringValue] length]]]
And it actually worked this time. Not sure why it didn't the first time, though I think I might have typed %d instead of %u by mistake.
However I still don't understand why the code I posted at the top of my original post doesn't work, and I have no idea what the errors mean, and I'd very much like to know because it seems like I'm missing something important there.