Different behavior - NSDictionary on iPad vs. iOS Simulator - objective-c

int a = 0;
NSDictionary* d = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:a], #"A",
[NSNumber numberWithInt:a+=1], #"B",
[NSNumber numberWithInt:a+=1], #"C",
nil];
NSLog(#"%#", d);
Result on iPad:
{
A = 0;
B = 1;
C = 2;
}
Result in iOS Simulator:
{
A = 2;
B = 2;
C = 1;
}
Can anyone reproduce the result, or even better, explain it?

This is undefined behavior. There is no right answer for those values. The order in which effects are applied is undefined between sequence points, and the commas in an argument list are not sequence points.

The order of evaluation of arguments to a function or method is undefined and determined by the compiler. This means that there is no guarantee that the first number will be created before the second, and is inherited by objective-c from c. It appears that on the iPad, they are being executed in the same order that they are in the code, but for the simulator they are created in reverse order. This is the order of events:
iPad:
Evaluate first argument. This is a method. Its arguments must be processed:
The argument is the variable a. Use its current value (0).
Evaluate second argument. This is a constant string. Load its address.
Evaluate third argument. This is a method. Its arguments must be processed:
The argument is an expression: a += 1. Increment a and return its new value (1).
Evaluate fourth argument. This is a constant string. Load its address.
Evaluate fifth argument. This is a method. Its arguments must be processed:
The argument is an expression: a += 1. Increment a and return its new value (2).
Evaluate sixth argument. This is a constant string. Load its address.
Simulator:
Evaluate sixth argument. This is a constant string. Load its address.
Evaluate fifth argument. This is a method. Its arguments must be processed:
The argument is an expression: a += 1. Increment a and return its new value (1).
Evaluate fourth argument. This is a constant string. Load its address.
Evaluate third argument. This is a method. Its arguments must be processed:
The argument is an expression: a += 1. Increment a and return its new value (2).
Evaluate second argument. This is a constant string. Load its address.
Evaluate first argument. This is a method. Its arguments must be processed:
The argument is the variable a. Use its current value (2).
Since the calling convention of i386, which is used by the simulator, is that arguments are passed on the stack in reverse order, I would guess that this is why the compiler evaluates them in reverse order.
To fix your problems, you should create the NSNumber objects before creating the dictionary, forcing the compiler to use the order you want.
int a = 0;
NSNumber *numA = [[NSNumber alloc] initWithInt:a];
a += 1;
NSNumber *numB = [[NSNumber alloc] initWithInt:a];
a += 1;
NSNumber *numC = [[NSNumber alloc] initWithInt:a];
NSDictionary *d = [NSDictionary dictionaryWithObjectsAndKeys:numA, #"A", numB ,#"B", numC, #"C", nil];
[numA release];
[numB release];
[numC release];
NSLog(#"%#", d);

Related

Comparison between pointer and int (id to int) - Object wrapping still doesn't work [duplicate]

So my problem is this:
I am receiving a JSON string from across the network. When decoded (using SBJSON libraries), it becomes an NSDictionary that SHOULD contain a number of some sort for the key 'userid'. I say 'should' because when I compare the value to an int, or an NSINTEGER, or NSNumber, it never evaluates correctly.
Here is the comparison in code:
NSDictionary *userDictionary = [userInfo objectAtIndex:indexPath.row];
if ([userDictionary objectForKey:#"userid"] == -1) {
//Do stuff
}
The value inside the dictionary I am testing with is -1. When I print it out to console using NSLog it even shows it is -1. Yet when I compare it to -1 in the 'if' statement, it evaluates to false when it should be true. I've even tried comparing to [NSNumber numberWithInt: -1], and it still evaluates to false.
What am I doing wrong? Thanks in advance for your help!
You are comparing a pointer to an object, not an integer itself. You must first convert the object into an integer:
if ([[userDictionary objectForKey:#"userid"] integerValue] == -1)
{
//Do stuff
}

indexOfObjectIdenticalTo: - Getting junk value

NSString *yes0 = #"yes";
NSString *yes1 = #"YES";
NSString *yes2 = #"Yes";
NSString *yes3 = [NSString stringWithFormat:#"%#",yes1];
NSArray *yesArray = [NSArray arrayWithObjects:#"yes", #"YES", #"Yes", #"YES", nil];
NSArray *yesArray1 = [NSArray arrayWithObjects:yes0, yes1, yes2, yes3, nil];
NSUInteger index;
index = [yesArray indexOfObjectIdenticalTo:yes3];
NSLog(#"\nindex : %d", index); // 1st Output
index = [yesArray1 indexOfObjectIdenticalTo:yes3];
NSLog(#"\nindex : %d", index); // 2nd Output
1st Output
Output i receive is: "2013-07-18 16:42:04.896 Collections - Arrays[2778:f803] index : 2147483647"
2nd Output
Output i receive is: "2013-07-18 16:42:04.896 Collections - Arrays[2778:f803] index : 3"
I get confused due to the outputs i receive. Kindly explain me the process why i get the junk value during the output 1. According me, both arrays are identical with objects. Then why this junk value.
Also having a question:
According to apple, indexOfObjectIdenticalTo: returns - The lowest
index whose corresponding array value is identical to anObject.
Upto my understanding, in yesArray1 - yes1 is same as yes3. My expecting value of output 2 is 1 which gets contradict with the actual printed value (3). Also please let me understand it properly why it is printing 3 rather than 1.
First off, it's not a "junk value", it's the NSNotFound constant.
The first index is not found because yes3 is not in yesArray (it's only in yesArray1) – note that indexOfObjectIdenticalTo: checks for pointer identity, not object equality (unlike indexOfObject:).
indexOfObjectIdenticalTo checks for pointer equality, not object equality. 2147483647 isn't a junk value, it's the value of NSNotFound (which is the defined result if no index is found).
indexOfObjectIdenticalTo : checks for pointer equality
indexOfObject: checks Object equality.
Good Luck !!!

arc4random in NSNumber giving negative values?

I'm creating a random number and storing it in a NSNumber object like this:
NSNumber *index = [NSNumber numberWithUnsignedInt:arc4random_uniform(2^32-1)];
I also tried:
NSNumber *index = [NSNumber numberWithUnsignedInt:arc4random_uniform(4294967295)];
NSNumber *index = #(arc4random_uniform(4294967295));
At some point I'm also assigning the number 1 like this:
NSNumber *index = #(1);
This should give me only positive numbers.
Later on, I print out these numbers like this:
NSString *string = [NSString stringWithFormat:#"%#", index];
This gives me some negative values for the random numbers and 1 is being printed as 1. So I though maybe if I do:
NSString *string = [NSString stringWithFormat:#"%u", index.unsignedIntValue];
I'll get only positive numbers - which I do - but now 1 is suddenly being printed as some large positive number, also.
What's going on here? How can I correctly store a u_int32 (which arc4random returns) in a NSNmber and make sure that they are only positive?
Use
NSNumber *index = [NSNumber numberWithUnsignedInt:arc4random_uniform(exp2(32)-1)];
I never get any negative numbers. arc4random_uniform(x) always returns a number between 0 and x, and the stringvalue of the NSNumber generated from it is correct.
EDIT: replaced exp2(31) with exp2(32)
You said in a comment that the index is stored in a Core Data entity as an "Integer 32" attribute, and I assume that is where the problem comes from.
Core Data dynamically generates getter and setter methods for all attributes (and relationships) of managed object classes. These accessor methods are different from the "usual" #synthesized accessor methods which are backed up by an instance variable.
For an "Integer 32" attribute, Core Data uses a (signed) integer for the attribute, and when you set a value, it is just cast or truncated to int. Example:
e.index = [NSNumber numberWithUnsignedInt:0xFFFFFFF0U];
// This would give the same result:
// e.index = [NSNumber numberWithLongLong:0x1234FFFFFFF0LL];
NSNumber *val = e.index;
NSLog(#"value=%#, type=%s", val, [val objCType]);
// Output: value=-16, type=i
The output type=i shows that the value contains an int.
If you need unsigned integers in the range 0 .. 2^32-1, then you can either (as you already did) use unsignedIntValue:
unsigned x = [val unsignedIntValue];
or store the attribute as "Integer 64".
Remarks:
I am fairly sure that this is not a problem of arc4random_uniform.
In your first code example arc4random_uniform(2^32-1), you should note that ^ is exclusive-or, not exponentiation.

How to check if NSString format contains the same number of specifiers as there are variadic arguments?

To ensure that a formatted string returned by NSString initWithFormat:arguments: is as expected, I need to determine if there are the same number of format specifiers as arguments. Below is a (slightly contrived and highly edited) example:
- (void)thingsForStuff:(CustomStuff)stuff, ...
{
NSString *format;
switch (stuff)
{
case CustomStuffTwo:
format = #"Two things: %# and %#";
break;
case CustomStuffThree:
format = #"Three things: %#, %#, and %#";
break;
default:
format = #"Just one thing: %#";
break;
}
va_list args;
va_start(args, method);
// Want to check if format has the same number of %#s as there are args, but not sure how
NSString *formattedStuff = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
NSLog(#"Things: %#", formattedStuff);
}
Using this method, [self thingsForStuff:CustomStuffTwo, #"Hello", #"World"] would log
"Two things: Hello and World"
...but [self thingsForStuff:CustomStuffTwo, #"Hello"] would log
"Two things: Hello and "
...something that would be preferred to be caught before it happens.
Is there a way to count the format specifiers in a string, preferably something lightweight/inexpensive?
Well, I created my own regex, I have no idea if it's going to catch all of them, and may end finding some false positives, but seems to be working for me:
static NSString *const kStringFormatSpecifiers =
#"%(?:\\d+\\$)?[+-]?(?:[lh]{0,2})(?:[qLztj])?(?:[ 0]|'.{1})?\\d*(?:\\.\\d+)?[#dDiuUxXoOfeEgGcCsSpaAFn]";
You can count the number of arguments using:
NSRegularExpression *regEx = [NSRegularExpression regularExpressionWithPattern: kStringFormatSpecifiers options:0 error:nil];
NSInteger numSpecifiers = [regEx numberOfMatchesInString: yourString options:0 range:NSMakeRange(0, yourString.length)];
Is there a way to count the format specifiers in a string, preferably
something lightweight/inexpensive?
Nope -- really isn't. At least, not if you want it to work across all possible format strings. You would have to duplicate the parser that is used by stringWithFormat:. I.e. don't try to validate everything.
You could count the number of %, but that would not catch things like %% or other special cases. That may be good enough for your purposes.
Because of the way C and Objective-C handle variadic functions/methods like yours, you cannot in general tell how many arguments the user has provided.
Here are two ways to handle your situation.
First, look for another way to do this. The number of arguments you pass to the method is determined at compile-time. So maybe instead of using a variadic method, you should just have three methods:
- (void)doStuff:(CustomStuff)stuff withThing:(Thing *)thing;
- (void)doStuff:(CustomStuff)stuff withThing:(Thing *)thing1 thing:(Thing *)thing2;
- (void)doStuff:(CustomStuff)stuff withThing:(Thing *)thing1 thing:(Thing *)thing2 hatWearer:(Cat *)cat;
And you select the right method to call at compile-time based on how many arguments you want to pass, eliminating the switch statement entirely.
Second, I see that your predefined format strings only use the %# format. Does this mean that you expect the user to only pass objects to your method (aside from the (CustomStuff)stuff argument)?
If the user will only pass objects to your method, and you require those arguments to be non-nil, then you can get the compiler to help you out. Change your method to require the user to pass nil at the end of the argument list. You can tell the compiler that the argument list has to be nil-terminated by declaring the method (in your #interface) like this:
#interface MyObject : NSObject
- (void)thingsForStuff:(CustomStuff)stuff, ... NS_REQUIRES_NIL_TERMINATION
#end
Now the compiler will warn the user “Missing sentinel in method dispatch” if he calls your method without putting a literal nil at the end of the argument list.
So, having changed your API to require some non-nil arguments followed by a nil argument, you can change your method to count up the non-nil arguments like this:
- (void)thingsForStuff:(CustomStuff)stuff, ... {
int argCount = 0;
va_list args;
va_start(args, stuff);
while (va_arg(args, id)) {
++argCount;
}
va_end(args)
int expectedArgCount;
NSString *format;
switch (stuff) {
case CustomStuffTwo:
expectedArgCount = 2;
format = #"Two things: %# and %#";
break;
case CustomStuffThree:
expectedArgCount = 3;
format = #"Three things: %#, %#, and %#";
break;
// etc.
}
NSAssert(argCount == expectedArgCount, #"%# %s called with %d non-nil arguments, but I expected %d", self, (char*)_cmd, argCount, expectedArgCount);
va_start(args, stuff);
NSString *formattedStuff = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
NSLog(#"Things: %#", formattedString);
}
You could count the number of format specifiers, but IIRC you will never be able to count the number of arguments passed into a variable-argument method. This is because of the way C pushes arguments on the stack without specifying how many it has pushed.
Most functions overcome this by requiring that the last argument be nil or some kind of terminator (see [NSArray arrayWithObjects:]). There's even a macro that allows the compiler to check this and emit a warning at compile time.
You can use NS_FORMAT_FUNCTION at the end of your function prototype, as in stringWithFormat method of NSString.
So your method's prototype should be like this:
- (void)thingsForStuff:(CustomStuff)stuff, ... NS_FORMAT_FUNCTION(1,2);
long specifierCount = [myFormatString componentsSeparatedByString:#"%"].count;
This will get you close. Its just a simple split. You would have to account for escaped % values.

Is there any way to pass an NSArray to a method that expects a variable number of arguments, such as +stringWithFormat:

Some context: I'm trying to clean up some of my FMDB code. My one table has a lot of columns and the method in FMDB I need to use is one that expects a variable number of arguments, similar to NSString's class method +stringWithFormat:.
An example:
[db executeUpdate:#"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
#"hi'", // look! I put in a ', and I'm not escaping it!
[NSString stringWithFormat:#"number %d", i],
[NSNumber numberWithInt:i],
[NSDate date],
[NSNumber numberWithFloat:2.2f]];
When a table only has 5 columns it's not that bad but when a column has 20+ it starts to get hairy.
What I'd like to do is create a dictionary with all db abstraction information and build these queries dynamically. My question is... How in Objective-C do I fake out that method expecting a variable number of arguments and instead perhaps hand it an NSArray?
Related info:
How can I write a method that takes a variable number of arguments, like NSString's +stringWithFormat:?
(Edit: This worked back in the GCC days. It doesn't under Clang as of Xcode 4.6.)
Get the objects in the array into a C array, then treat that as a varargs list:
//The example input array
int i = 42;
NSArray *array = [NSArray arrayWithObjects:
[NSString stringWithFormat:#"number %d", i],
[NSNumber numberWithInt:i],
[NSDate date],
[NSNumber numberWithFloat:2.2f],
nil];
//The example destination (using NSString so anyone can test this)
NSString *string = nil;
//The intermediary C array
NSObject **arrayObjects = malloc(sizeof(NSObject *) * [array count]);
if (arrayObjects) {
//Fill out the C array.
[array getObjects:arrayObjects];
//Use the C array as a va_list.
string = [[[NSString alloc] initWithFormat:#"%# %# %# %#" arguments:(va_list)arrayObjects] autorelease];
free(arrayObjects);
}
NSLog(#"string: %#", string);
Output:
2009-03-26 20:10:07.128 NSArray-varargs[606:10b] string: number 42 42 2009-03-26 20:10:07 -0700 2.2
In your case, you'll use the -[FMDatabase executeUpdate:arguments:] method.
It might be easier to just make a category on FMDatabase that takes an array and does the updates. You should be able to copy most of executeUpdate to do it.
I think NSInvocation may do what you're looking to do.
Just be careful when calling setArgumentForIndex because args 0 and 1 are implicit ones that Obj-C fills in, where arg 2 is the first "real" arg that you're passing.
This may not be the example you're looking for. But in this case I'd put your string values into an array and then use [theArray componentsJoinedByString:#","] to turn them into your sql argument list.