Objective-C Phrasebook: Numbers
- Storing Numbers in Collections
- Performing Decimal Arithmetic
- Converting Between Strings and Numbers
- Reading Numbers from Strings
One of the big differences between Objective-C and Smalltalk is that Objective-C inherits the full range of primitive (non-object) C types. These are, in ascending order of size, char, short, int, long and long long integers, with both signed and unsigned variants, as well as two floating-point types: float and double.
These all behave exactly as they do in C, complete with type promotion rules. You'll also find that Objective-C compilers support a long double type, which is architecture-dependent.
Note that this is very similar to Java, where you have a small selection of non-object types, but with some very important differences. In Java, the intrinsic types are defined to be a fixed size. In C, they are defined to have a minimum precision. For example, the specification says that an int has "the natural size suggested by the architecture of the execution environment," whereas in Java it is explicitly defined as a "32-bit signed two's complement integer."
As well as the primitive types, C supports defining new names for the existing types via the typedef keyword. The most common reason for this is that the specification does not require a particular size for any of the standard types, merely that each must be at least as big as the previous one. In particular, there are platforms currently deployed where int is 16, 32, and 64 bits, so you can't rely on any specific size for these.
OS X supports ILP32 and LP64 modes. This shorthand is used to describe which of the C types have which sizes. ILP32 means that ints, longs, and pointers are 32 bits. LP64 means that longs and pointers are 64-bit quantities, and that, implicitly, other values are smaller. Microsoft Windows, in contrast, is an LLP64 platform on 64-bit architectures; both int and long remain 32 bits and only pointers and long longs are 64 bits. This causes a problem if you assumed that you could safely cast a pointer to long—something that works on almost every platform in the world, including Win32, but does not work on Win64.
The problem of casting a pointer to an integer is a serious one. The long long type is at least 64 bits, so on any current platform it is guaranteed to be big enough to store any pointer, but on any 32- or 16-bit platform it can be much too big. C99 introduced the intptr_t typedef, which is exactly the size of a pointer. Apple introduced an equivalent: NSInteger. This is used throughout the Cocoa frameworks and is always the same size as a pointer. There is also an unsigned version, NSUInteger.
In GUI code, you will often come across CGFloat or NSFloat. These are equivalent to each other. Both are the size of a pointer, making them floats on 32-bit platforms and doubles on 64-bit ones.
Storing Numbers in Collections
From: numberInArray.m
6NSMutableArray *array = [NSMutableArray array];
7[array addObject: [NSNumber numberWithInt: 12]];
All of the standard Objective-C collection classes let you store objects, but often you want to store primitive types in them as well. The solution to this is boxing—wrapping a primitive type up in an object.
The NSValue class hierarchy is used for this. NSValue is a class designed to wrap a single primitive value. This class is quite generic, and is an example of a class cluster. When you create an instance of an NSValue, you will get back some subclass, specialized for storing different kinds of data. If you store a pointer in an NSValue, you don't want the instance to take up as much space as one containing an NSRect—a C structure containing four NSFloats.
One concrete subclass of NSValue is particularly important: NSNumber. This class is intended to wrap single numerical values and can be initialized from any of the C standard integer types.
The designated constructor for both of these classes is +valueWithBytes:objCType. The first argument is a pointer to some value and the second is the Objective-C type encoding of the type. Type encodings are strings representing a particular type. They are used a lot for introspection in Objective-C; you can find out the types of any method or instance variable in a class as a type encoding string and then parse this to get the relevant compile-time types.
You can get the type encoding of any type with the @encode () directive. This is analogous to sizeof () in C, but instead of returning the size as an integer it returns the type encoding as a C string. One very convenient trick when working with type encodings is to use the typeof() GCC extension. This returns the type of an expression. You can combine it with @encode (), like this:
NSValue *value = [NSValue valueWithBytes: &aPrimitive objCType: @encode(typeof(aPrimitive))];
This snippet will return an NSValue wrapping aPrimitive, and will work regardless of the type of the primitive. You could wrap this in a macro, but be careful not to pass it an expression with side effects if you do.
Note that you have to pass a pointer to the primitive value. This method will use the type encoding to find out how big the primitive type is and will then copy it.
More often, you will use one of the other constructors. For example, if you want to create an NSNumber instance from an integer, you would do so like this:
NSNumber *twelve = [NSNumber numberWithInt: 12];
The resulting object can then be added to a collection. Unlike NSValue, NSNumber instances are ordered, so you can sort collections containing NSNumber instances.
From: numberArray.m
6NSArray *a = [NSArray arrayWithObjects:
7[NSNumber numberWithUnsignedLongLong:
ULLONG_MAX],
8[NSNumber numberWithInt: -2],
9[NSNumber numberWithFloat: 300.057],
10[NSNumber numberWithInt: 1],
11[NSNumber numberWithDouble: 200.0123],
12[NSNumber numberWithLongLong: LLONG_MIN],
13nil];
14NSArray *sorted =
15[a sortedArrayUsingSelector: @selector(compare
:)];
16NSLog(@"%@", sorted);
The numberArray.m example stores a group of NSNumber instances in an array and then sorts them using the -compare: selector. As you can see from the output, the ordering is enforced irrespective of how the number was created.
Output from: numberArray.m
12010-03-15 14:50:48.166 a.out[51465:903] (
2"-9223372036854775808"
, 3"-2"
, 41,
5"200.0123"
, 6"300.057"
, 718446744073709551615
8)