NSValue to NSNumber conversion crash
-
03-07-2021 - |
Question
I want to convert NSValue to NSNumber. What is wrong with this code?
char value[4];
[myNSValue getValue:(void*)value];
NSNumber *number = [[NSNumber alloc] initWithBytes:(void*)value objCType:[myNSValue objCType]];
NSTimeInterval duration = [number intValue];
It causes a crash on the last line. What could happen here?
Solution
Please read my comment above for further explanation.
To achieve what you want, simply init an NSString with your (char *) and then invoke NSString's intValue. - Assuming you know the type, if not look at the comments below -
Cheers
OTHER TIPS
As @ale0xB diagnosed in the comments above, NSNumber
doesn't actually provide its own implementation of the -initWithBytes:objCType:
method; so, when you invoke that selector, you're actually getting the implementation that comes from NSNumber
's base class NSValue
. There is no1 difference between [[NSNumber alloc] initWithBytes:foo objCType:bar]
and [[NSValue alloc] initWithBytes:foo objCType:bar]
— they both call the same method implementation, which releases the self
it was given and returns a new object which is generally of type NSConcreteValue
(an undocumented subclass of NSValue
but not of NSNumber
).
To be even clearer:
#import <assert.h>
#import <Foundation/Foundation.h>
int main()
{
id x;
x = [NSNumber numberWithInt:42];
assert( [x isKindOfClass:[NSNumber class]] );
assert( [x respondsToSelector:@selector(intValue)] );
int fortytwo = 42;
x = [[NSNumber alloc] initWithBytes:&fortytwo objCType:@encode(int)];
assert( [x isKindOfClass:[NSValue class]] );
assert( ! [x isKindOfClass:[NSNumber class]] ); // yikes!
assert( ! [x respondsToSelector:@selector(intValue)] ); // your particular problem
}
However, categories to the rescue! Martin Häcker has written a category NSNumber(CreatingFromArbitraryTypes)
which can be adapted to do what you want. See the public-domain source code here.2 The basic idea is to special-case every possible type-encoding:
- if you're trying to encode an
"i"
, then dispatch to-numberWithInt:
; - if you're trying to encode an
"f"
, then dispatch to-numberWithFloat:
;
and so on. This is tedious; but once you've written the code once, you can use the category from then on and simply call [NSNumber numberWithValue:myNSValue]
.
(1 – More or less. There's no difference significant in this context, I'd say.)
(2 – That code has a couple of bugs, especially in later revisions. See my patch here.)