解决NSInteger< - > NSNumber问题

时间:2011-01-13 19:15:12

标签: iphone objective-c cocoa types

我编写了一个大型社交网络iPhone应用程序,我遇到的最大问题之一是NSInteger(以及所有其他NS非对象类型)不是一等公民。这个问题源于这样一个事实,显然,他们没有代表零值。

这会产生两个主要问题:

  1. 从集合中存储/检索时,转换为NSNumber和从NSNumber转换的大量开销和不透明度。
  2. 不能代表零。通常情况下,我希望能够代表“未设置”值。
  3. 解决此问题的一种方法是始终使用NSNumber,但这会让人非常困惑。在User模型对象中,我将有大约20个不同的NSNumber,并且没有简单的方法来判断每个NSNumber是浮点数,整数,bool等。

    以下是我对潜在解决方案和优缺点的看法。我并没有真正卖掉它们,所以我想我会要求反馈和/或解决这个问题。

    1. 继续使用NSInteger类型,只需使用NSIntegerMax表示nil。
      PRO - 减少内存开销 PRO - 清晰打字
      CON - NSIntegerMax并不是真的。如果程序员不小心或不了解此约定,则无效值可能会泄漏到显示层中 CON - 无法在没有转换进出的情况下将它们存储在集合中(
    2. 使用NSNumber并使用匈牙利表示法指定类型(例如NSNumber fHeight,NSNumber iAge)
      PRO - 一等公民 PRO - Nil问题解决了 CON - 增加内存开销
      CON - 丢失编译器类型检查
      CON - 匈牙利表示法有争议

    3. 编写我自己的第一类原始对象类型(想想Java http://developer.android.com/reference/java/lang/Integer.html
      PRO - 一等公民 PRO - Nil问题解决了 PRO - 保持编译器类型检查
      PRO - 对象将比NSNumber更简单。内部存储将特定于数据类型 CON - 增加内存开销
      CON - 牺牲一点代码的可移植性和兼容性
    4. 寻找一个有利于这些技术之一的令人信服的论据,或者我没有想到你是否有一个。


      更新

      我已经开始了,开始了一个开源项目(Apache 2.0),我将在其中拉动一些内部课程,因为我有时间。它目前包括一些更常见的本机数据类型(BOOL,CGFloat,NSInteger,NSUInteger)的对象包装器。我们选择这样做是因为它将这些数据类型升级为具有严格类型的一等公民。也许你不同意这种方法,但它对我们来说效果很好,所以如果你愿意,可以随意使用它。

      我正在添加我们发现的其他类,包括磁盘支持的LRU缓存,“Pair”对象,低内存释放池等。

      享受 github - Zoosk/ZSFoundation

4 个答案:

答案 0 :(得分:5)

nil的概念表示为NSInteger的最常见惯例是使用NSNotFound值。事实上,这等于NSIntegerMax,但对于读者来说,这是一个代表缺少数字的哨兵值,这一点往往更为明显。在很多情况下,整个Cocoa使用它。一个常见的情况是NSRange的位置字段作为-rangeOfString:等的返回值。

答案 1 :(得分:4)

你可以尝试一下吗?

#define numberWithFloat(float f) \
  [NSNumber numberWithFloat:f]
#define floatFromNumber(NSNumber *n) \
  [n floatValue]

(见下面的原始答案)

这是NSNumber的另一件事,你不必检索你设置的内容。

例如

NSNumber *myInt = [NSNumber numberWithInteger:100];
float myFloat = [myInt floatValue];

完全有效。 NSNumber的优势在于它允许您“弱化”您的基元,使用compare:,使用isEqualTo:stringValue以便于显示。


[编辑]

用户@Dave DeLong说,NSNumber的子类别是一个Bad Idea没有多少工作。因为它是class cluster(意思是NSNumber是许多子类的抽象超类),所以如果对它进行子类化,则必须声明自己的存储。不推荐,感谢Dave指出这一点。

答案 2 :(得分:3)

作为替代方案,还有用于表示数字类型的NSDecimal结构。 NSDecimal(及其Objective-C类版本NSDecimalNumber)允许您表示真正的小数并避免浮点错误,因此它往往是recommended for dealing with things like currency

NSDecimal结构可以表示数字以及非数字状态(潜在的nil替换)。您可以使用NSDecimalIsNotANumber()查询NSDecimal是否不是数字,并在NSDecimalNumber的帮助下生成Not a Number值。

NSDecimals是faster to work with than NSDecimalNumbers,结构体不会带来与对象相同的内存管理问题。

但是,如果不使用临时NSDecimalNumber,就没有一种简单的方法可以将值转换为NSDecimal形式。 此外,许多可用于简单浮点数的数学函数(如三角函数)尚不可用于NSDecimal。我想编写自己的函数来添加一些这些功能,但是他们需要访问结构的内部字段。 Apple将这些标记为私有(使用下划线),但它们出现在标题中,我猜他们将来不太可能改变。

更新: Dave DeLong编写了一系列用于执行NSDecimal三角函数,根和其他操作的函数。我已经调整了这些以提供最多34位小数精度,这些函数的代码可以下载here。作为一个警告,这种精确性在性能方面是有代价的,但这些功能对于相当不频繁的计算应该没问题。

答案 3 :(得分:0)

我不明白为什么,如果你把东西存放为NSNumber,你需要关心它是什么类型。 NSNumber将选择适当的内部表示,然后根据您要求的格式进行任何必要的转换。

如果您无法从变量名称中分辨出哪种类型合适,则需要选择更好的名称。例如

numberOfChildren = [NSNumber numberWithFloat: 2.5];

显然很傻。

对于货币值,我绝对建议使用NSDecimalNumber(它是NSNumber的子类),但定义了基数10算术运算,因此您不必担心浮动舍入。