4

I am trying to pass a hex value as an unsigned int to a method using a dynamic link. The value I pass as a parameter is getting corrupted somehow. What is happening?

- (void)callPerformSelector
{
    NSNumber *argument = [NSNumber numberWithUnsignedInt:(unsigned int)0xFFFFFFFF];
    SEL selector = NSSelectorFromString(@"testPerformSelector:");
    NSLog(@"testPerformSelector object %@", argument);
   [self performSelector:selector withObject:argument];
}

- (void)testPerformSelector:(unsigned int) arg1
{
    NSLog(@"testPerformSelector unsigned int %u", arg1);
    NSLog(@"testPerformSelector hex %X", arg1);
}

Output is:

testPerformSelector object 4294967295
testPerformSelector unsigned int 4294967283
testPerformSelector hex FFFFFFF3
Simon Fry
  • 141
  • 1
  • 6

3 Answers3

5

Because it should be:

- (void)callPerformSelector
{
    NSNumber *argument = @0xFFFFFFFF;
    SEL selector = @selector(testPerformSelector:);
    NSLog(@"testPerformSelector object %@", argument);
   [self performSelector:selector withObject:argument];
}

- (void)testPerformSelector:(NSNumber *) arg1
{
    NSLog(@"testPerformSelector unsigned int %u", arg1.unsignedIntValue);
}

unsigned int and NSNumber * is two different things

Cy-4AH
  • 4,370
  • 2
  • 15
  • 22
2

There's an easy reason and a complex reason.

Simple reason: Why this doesn't work. The first argument to the target of a performSelectorWithObject must be an object. You are specifying a pointer to an unsigned integer in your function signature but then passing an object (NSNumber) when you call it. So instead of:

- (void)testPerformSelector:(unsigned int) arg1

you should have

- (void)testPerformSelector:(NSNumber *) arg1

You will then need to use NSNumber's unsignedIntValue to get the 0xFFFFFFFF out of the object.

The complex reason is much more interesting: Why this nearly works and looks like it loses a few bits. NSNumber is an object that wraps a numeric value this is very different from a raw numeric value. However NSNumber is implemented as a tagged pointer so although objective-c knows it is an object and treats it like an object, a subset of NSNumber values are implemented as tagged pointers where real values are coded into the "pointer" and the fact that this is not a normal pointer is indicated in the (otherwise always zero) bottom four bits of a pointer see Friday Q&A.

Rog
  • 17,070
  • 9
  • 50
  • 73
1

You can only pass objects to selectors and not primitive types, and therefore the selector should be:

- (void)testPerformSelector:(NSNumber *) arg1
{
    NSLog(@"testPerformSelector hex %x", [arg1 unsignedIntValue]);
}

Update: As pointed-out by @gnasher729, the reason the number passed appears to be -13 is because it's a tagged pointer.

Droppy
  • 9,691
  • 1
  • 20
  • 27