You’re reading Ry’s Objective-C Tutorial → Data Types |
The NSNumber
class is a lightweight, object-oriented wrapper around C’s numeric primitives. It’s main job is to store and retrieve primitive values, and it comes with dedicated methods for each data type:
NSNumber
*
aBool
=
[
NSNumber
numberWithBool:
NO
];
NSNumber
*
aChar
=
[
NSNumber
numberWithChar:
'z'
];
NSNumber
*
aUChar
=
[
NSNumber
numberWithUnsignedChar:
255
];
NSNumber
*
aShort
=
[
NSNumber
numberWithShort:
32767
];
NSNumber
*
aUShort
=
[
NSNumber
numberWithUnsignedShort:
65535
];
NSNumber
*
anInt
=
[
NSNumber
numberWithInt:
2147483647
];
NSNumber
*
aUInt
=
[
NSNumber
numberWithUnsignedInt:
4294967295
];
NSNumber
*
aLong
=
[
NSNumber
numberWithLong:
9223372036854775807
];
NSNumber
*
aULong
=
[
NSNumber
numberWithUnsignedLong:
18446744073709551615
];
NSNumber
*
aFloat
=
[
NSNumber
numberWithFloat:
26.99f
];
NSNumber
*
aDouble
=
[
NSNumber
numberWithDouble:
26.99
];
NSLog
(
@"%@"
,
[
aBool
boolValue
]
?
@"YES"
:
@"NO"
)
;
NSLog
(
@"%c"
,
[
aChar
charValue
])
;
NSLog
(
@"%hhu"
,
[
aUChar
unsignedCharValue
])
;
NSLog
(
@"%hi"
,
[
aShort
shortValue
])
;
NSLog
(
@"%hu"
,
[
aUShort
unsignedShortValue
])
;
NSLog
(
@"%i"
,
[
anInt
intValue
])
;
NSLog
(
@"%u"
,
[
aUInt
unsignedIntValue
])
;
NSLog
(
@"%li"
,
[
aLong
longValue
])
;
NSLog
(
@"%lu"
,
[
aULong
unsignedLongValue
])
;
NSLog
(
@"%f"
,
[
aFloat
floatValue
])
;
NSLog
(
@"%f"
,
[
aDouble
doubleValue
])
;
It may seem redundant to have an object-oriented version of all the C primitives, but it’s necessary if you want to store numeric values in anNSArray
, NSDictionary
, or any of the other Foundation Framework collections. These classes require all of their elements to be objects—they do not know how to interact with primitive values. In addition, NSNumber
makes it possible to pass numbers to methods like performSelector:withObject:
, as discussed in Selectors.
But, aside from the object-oriented interface, there are a few perks to using NSNumber
. For one, it provides a straightforward way to convert between primitive data types or get an NSString
representation of the value:
NSNumber
*
anInt
=
[
NSNumber
numberWithInt:
42
];
float
asFloat
=
[
anInt
floatValue
];
NSLog
(
@"%.2f"
,
asFloat
)
;
NSString
*
asString
=
[
anInt
stringValue
];
NSLog
(
@"%@"
,
asString
)
;
And, like all Objective-C objects, NSNumber
can be displayed with the %@
format specifier, which means that you can forget about all of the C-style specifiers introduced in the Primitives module. This makes life a tiny bit easier when trying to debug values:
NSNumber
*
aUChar
=
[
NSNumber
numberWithUnsignedChar:
255
];
NSNumber
*
anInt
=
[
NSNumber
numberWithInt:
2147483647
];
NSNumber
*
aFloat
=
[
NSNumber
numberWithFloat:
26.99f
];
NSLog
(
@"%@"
,
aUChar
)
;
NSLog
(
@"%@"
,
anInt
)
;
NSLog
(
@"%@"
,
aFloat
)
;
Xcode 4.4 introduced numeric literals, which offer a much more convenient alternative to the above factory methods. The NSNumber
version of BOOL
’s, char
’s, int
’s and double
’s can all be created by simply prefixing the corresponding primitive type with the @
symbol; however, unsigned int
’s, long
’s, and float
’s must be appended with the U
, L
, or F
modifiers, as shown below.
NSNumber
*
aBool
=
@NO
;
NSNumber
*
aChar
=
@'z'
;
NSNumber
*
anInt
=
@2147483647
;
NSNumber
*
aUInt
=
@4294967295
U
;
NSNumber
*
aLong
=
@9223372036854775807
L
;
NSNumber
*
aFloat
=
@26
.99F
;
NSNumber
*
aDouble
=
@26
.99
;
In addition to literal values, it’s possible to box arbitrary C expressions using the @()
syntax. This makes it trivial to turn basic arithmetic calculations into NSNumber
objects:
double
x
=
24.0
;
NSNumber
*
result
=
@(
x
*
.15
);
NSLog
(
@"%.2f"
,
[
result
doubleValue
])
;
It’s important to understand that NSNumber
objects are immutable—it’s not possible to change its associated value after you create it. In this sense, an NSNumber
instance acts exactly like a primitive value: When you need a new double
value, you create a new literal—you don’t change an existing one.
From a practical standpoint, this means you need to create a newNSNumber
object every time you change its value. For example, the following loop increments a counter
object by adding to its primitive value, then re-boxing it into a new NSNumber
and assigning it to the same variable.
NSNumber
*
counter
=
@0
;
for
(
int
i
=
0
;
i
<
10
;
i
++
)
{
counter
=
@([
counter
intValue
]
+
1
);
NSLog
(
@"%@"
,
counter
)
;
}
As you could probably imagine, this isn’t the most efficient way to work with numbers. In real applications, you should limit yourself to primitive numeric types for computationally intensive algorithms and wait as long as possible to store the result in an NSNumber
container.
While it’s possible to directly compare NSNumber
pointers, the isEqualToNumber:
method is a much more robust way to check for equality. It guarantees that two values will compare equal, even if they are stored in different objects. For example, the following snippet shows a common case of pointer comparison failure.
NSNumber
*
anInt
=
@27
;
NSNumber
*
sameInt
=
@27
U
;
// Pointer comparison (fails)
if
(
anInt
==
sameInt
)
{
NSLog
(
@"They are the same object"
);
}
// Value comparison (succeeds)
if
([
anInt
isEqualToNumber:
sameInt
])
{
NSLog
(
@"They are the same value"
);
}
If you need to check for inequalities, you can use the related compare:
method. Instead of a Boolean value, it returns an NSComparisonResult
, which is an enum
that defines the relationship between the operands:
Return Value | Description |
---|---|
NSOrderedAscending |
receiver < argument |
NSOrderedSame |
receiver == argument |
NSOrderedDescending |
receiver > argument |
The following example shows these enumerators in action.
NSNumber
*
anInt
=
@27
;
NSNumber
*
anotherInt
=
@42
;
NSComparisonResult
result
=
[
anInt
compare:
anotherInt
];
if
(
result
==
NSOrderedAscending
)
{
NSLog
(
@"27 < 42"
);
}
else
if
(
result
==
NSOrderedSame
)
{
NSLog
(
@"27 == 42"
);
}
else
if
(
result
==
NSOrderedDescending
)
{
NSLog
(
@"27 > 42"
);
}
This kind of object-oriented comparison may not be as convenient as the familiar <
, ==
, and >
operators, but abstracting them into methods affords a lot more flexibility to the Foundation Framework classes.
Sign up for my low-volume mailing list to find out when new content is released. Next up is a comprehensive Swift tutorial planned for late January.
You’ll only receive emails when new tutorials are released, and your contact information will never be shared with third parties. Click here to unsubscribe.