如何添加两个NSNumber对象?

86

现在这应该很简单,但是如何对两个NSNumber进行求和?类似于:

[one floatValue] + [two floatValue]

还有更好的方法吗?

6个回答

149

没有更好的方法,但如果可以避免,您确实不应该这样做。 NSNumber 作为一个封装器存在于标量数字中,因此您可以将它们存储在集合中并与其他NSObjects多态地传递它们。它们实际上并不用于存储数学上的数字。如果您在它们上执行数学运算,那么速度会比仅对标量执行操作要慢得多,这可能是为什么没有方便的方法的原因。

例如:

NSNumber *sum = [NSNumber numberWithFloat:([one floatValue] + [two floatValue])];

调用消息分派至少需要21条指令,再加上方法解包和重新封装值所需的代码量(大约几百行),才能执行一次有关数学的指令。

因此,如果您需要在字典中存储数字,请使用 NSNumber;如果您需要将可能是数字或字符串的参数传递给函数,请使用 NSNumber;但如果您只是想进行数学计算,请使用标量 C 类型。


1
我想补充一点,如果您需要存储数字集合,并且打算进行大量的数学计算 - 也许您真正需要的是适当数字类型的C风格数组? - Kendall Helmstetter Gelner
NSNumber的正确方法是'numberWithFloat:' - 或许自回答时以来已经发生了变化。 - Nick
如果你看一下,我在5年前就回答了这个问题,当时字面量还没有被添加。 - Louis Gerbarg
2
@clopez:“医生,我这样做会疼。” NSNumber是用于容器的封装类型,不适用于算术运算。如果要进行算术运算,请使用算术类型。 - Stephen Canon
当使用CoreData时,虽然速度很慢,但我能做什么呢? - Fidel López
显示剩余3条评论

50

NSDecimalNumberNSNumber的子类)拥有你所需要的所有好处:

 decimalNumberByAdding:
 decimalNumberBySubtracting:
 decimalNumberByMultiplyingBy:
 decimalNumberByDividingBy:
 decimalNumberByRaisingToPower:

如果需要考虑计算性能,那么请转换为C++的数组std::vector或类似类型。

现在我不再使用C-Arrays了;使用错误的索引或指针很容易导致崩溃,并且每次都需要配对使用new []和delete[]非常繁琐。


我们这样来说吧。在 C 语言中,尽量避免使用指针。Objective-C 的整个重点是在 C 语言的基础上增加一层,使你永远不必担心它。C 并不是一种可以直接使用的语言,它需要进行“管理”。 - user4951
1
为什么要将NSDecimalNumber转换回NSNumber?因为NSDecimalNumber 本身就是 NSNumber:@interface NSDecimalNumber : NSNumber - Steven Fisher
我无法相信这个评论有38个赞。要么详细说明为什么C++的std::vector更好,要么就不要评论。目前这个评论看起来很有偏见。 - Austin
NSDecimalNumber是NSNumber的一个子类。@ohho - Oritm
数组与问题或您的答案有什么关系? - Motti Shneor
显示剩余3条评论

12

你可以使用

NSNumber *sum = @([first integerValue] + [second integerValue]);

编辑: 正如ohho所观察到的,此示例是将保存整数值的两个NSNumber实例相加。如果您想要相加保存浮点值的两个NSNumber,您应该执行以下操作:

NSNumber *sum = @([first floatValue] + [second floatValue]);

"@"符号的作用是什么?我不确定它在这个上下文中的作用。" - Mike Meyers
啊,我现在明白了……它是一个NSNumber字面量,就像这里描述的一样:https://dev59.com/gFXTa4cB1Zd3GeqP1WmB#11120371 - Mike Meyers
是的,@mikemeyers,它是将原始类型如intlong或其大兄弟typedefNSInteger包装成NSNumber *对象的字面量。 - nemesis
integerValue截断了float。如何为此回答点赞? - ohho
@ohho 这只是一个简单的例子。如果你想保留数字的小数部分,为什么要使用 integerValue - nemesis

9
当前最高票答案会导致难以诊断的错误和精度损失,因为使用了浮点数。如果您正在对NSNumber值进行数字操作,则应首先转换为NSDecimalNumber,并使用这些对象执行操作。

来自文档:

NSDecimalNumber是NSNumber的不可变子类,提供了一个面向对象的基于10进制算术的封装。一个实例可以表示任何可以表示为mantissa x 10^exponent的数字,其中mantissa是一个长达38位的十进制整数,指数是从-128到127的整数。

因此,您应该通过[NSNumber decimalValue]将NSNumber实例转换为NSDecimalNumbers,执行所需的算术运算,然后在完成后重新分配给NSNumber。
在Objective-C中:
NSDecimalNumber *a = [NSDecimalNumber decimalNumberWithDecimal:one.decimalValue]
NSDecimalNumber *b = [NSDecimalNumber decimalNumberWithDecimal:two.decimalValue]
NSNumber *result = [a decimalNumberByAdding:b]

在Swift 3中:

let a = NSDecimalNumber(decimal: one.decimalValue)
let b = NSDecimalNumber(decimal: two.decimalValue)
let result: NSNumber = a.adding(b)

1

为什么不使用NSxEpression呢?

NSNumber *x = @(4.5), *y = @(-2);

NSExpression *ex = [NSExpression expressionWithFormat:@"(%@ + %@)", x, y];
NSNumber *result = [ex expressionValueWithObject:nil context:nil];

NSLog(@"%@",result); // will print out "2.5"

您还可以构建一个NSExpression,以便重复使用它来评估不同的参数,就像这样:

NSExpression *expr = [NSExpression expressionWithFormat: @"(X+Y)"];
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:x, @"X", y, @"Y", nil];
NSLog(@"%@", [expr expressionValueWithObject:parameters context:nil]);

例如,我们可以循环评估相同的解析表达式,每次使用不同的“Y”值:
 for (float f=20; f<30; f+=2.0) {
    NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:x, @"X", @(f), @"Y", nil];
    NSLog(@"%@", [expr expressionValueWithObject:parameters context:nil]);
 }

-2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接