【问题标题】:Why is the row property of NSIndexPath a signed integer?为什么 NSIndexPath 的 row 属性是有符号整数?
【发布时间】:2012-11-27 13:24:25
【问题描述】:

为什么 NSIndexPath 的 row 属性是有符号整数?

它是否可以采用“有效”的负值?

编辑

直到今天我设置 LLVM 来检查符号比较时,我才考虑过这个问题。这使得编译器只要有indexPath.row <= [someArray count] 或类似名称就会发出警告。

【问题讨论】:

  • 它是只读的,所以你不关心它:) 为什么行需要负值?
  • 行不需要负值,我只是想知道为什么行不是 NSUInteger。
  • "它是只读的,所以你不用管它";你应该,initWithRow:section: 接受NSIntegers。
  • +1 好问题,我猜(开玩笑)是因为 Apple 不希望您使用超过 2,147,483,647 个单元格的表格!
  • @0x7fffffff♦ - 你成就了我的一天 :)

标签: objective-c ios6 xcode4.5


【解决方案1】:

如果使用负数会怎样?

使用负值是不明智的,如果你这样做,你会得到疯狂的结果

NSIndexPath* path = [NSIndexPath indexPathForRow:-2 inSection:0];

上面的结果是 0 的部分和 4294967294 的行(在我看来,这就像 NSUInteger 的下溢!)请注意,这仅发生在 UIKit Additions 类别中,而不是在 NSIndexPath 中本身。看看NSIndexPath 背后的概念,持有负值真的没有意义。那为什么呢?

(可能)原因

OS X 的核心对象NSIndexPath 使用NSUIntegers 作为其索引,但UIKit Addition 使用NSInteger。该类别仅建立在核心对象之上,但使用NSInteger 而非NSUInteger 不会提供任何额外功能。

为什么会这样,我不知道。我的猜测(我规定 guess),这是第一次启动 iOS 时的一个幼稚的 API 失误。当UITableView 在iOS 2 中发布时,它使用NSIntegers 来表示各种各样的东西(例如numberOfSections)。想一想:这在概念上没有意义,你不能有负数的部分。现在即使在 iOS 6 中,它仍然使用NSInteger,因此不会破坏以前的应用程序与表格视图的兼容性。

除了UITableView,我们还添加了NSIndexPath,它们与表视图一起用于访问它的行等。因为它们必须一起工作,所以它们需要兼容的类型(在本例中为 NSInteger)。

将类型更改为NSUInteger 会破坏很多东西,并且为了安全的 API 设计,所有内容都需要重命名,以便 NSInteger 和 NSUInteger 对应物可以安全地并排工作。 Apple 可能不想要这种麻烦(开发人员也不想要!),因此他们将其保留在 NSInteger

【讨论】:

  • iOS 4 和 iOS 5 之间的行为没有改变吗?我相信我正在修复一些关于过渡的警告。
  • 不确定,他们的 API 合同自 iOS 2 以来肯定没有更新。行为可能已经改变,但这不应该破坏 API 和用户之间的合同。警告可能是编译器更聪明地警告用户他们可能会做什么。
  • 很好地为问题添加了经验证据,但不是答案。
  • 答案是,我们不知道(写成“为什么它会这样工作,因为它允许 NSInteger 我不知道。”)。在库比蒂诺之外没有明确的文档或理由可以解释为什么会这样,我们只能推测。我相信所提供的 经验 证据是合理的,因为使用常识我真的相信这是 API 设计中的一个失误。对部分和行使用负索引是没有意义的,但是 UITableView 在发布时使用了这些类型。例如,numberOfSections 返回一个有符号整数是很疯狂的。
【解决方案2】:

一个可能的原因是无符号类型很容易下溢。例如,我的代码中有一个用于笔划宽度的NSUInteger 变量。我需要围绕用这个笔画绘制的点创建一个“信封”,因此代码如下:

NSUInteger width = 3;
CGRect envelope = CGRectInset(CGRectZero, -width, -width);
NSLog(@"%@", NSStringFromCGRect(envelope));

对于无符号类型,这将输出{{inf, inf}, {0, 0}},对于有符号整数,您将得到{{-3, -3}, {6, 6}}。原因是width 变量之前的一元减号会产生下溢。这对某些人来说可能很明显,但会让很多程序员感到惊讶:

NSUInteger a = -1;
NSUInteger b =  1;
NSLog(@"a: %u, b: %u", a, -b); // a: 4294967295, b: 4294967295

因此,即使在使用负值没有意义的情况下(笔画宽度不能为负),在负值上下文中使用值也是有意义的,这会导致下溢。切换到有符号类型会减少意外,同时仍保持范围相当高。听起来是个不错的妥协。

【讨论】:

  • 对于 UITableView 返回 NSInteger 的方法,如 (NSInteger)numberOfSections 对您有意义吗?如果您需要执行算术运算,请自行将NSUInteger 转换为NSInteger。该表(及其各自的NSIndexPath)不需要关心用户级算法。
  • 我当然可以自己投号码,关键是我需要意识到问题所在。无符号类型在这里真的没有任何优势,不是吗?
  • 概念上是正确的;你不能有负数的部分,因此类型应该尝试强制执行。
  • 无符号类型不强制执行任何操作,编译器对NSUInteger foo = -1 很满意。 (对此有警告吗?)这是一种记录措施,仅此而已。我要说的是,考虑到符号溢出可能会让人头疼,我可以理解为什么 Apple 宁愿默认使用有符号类型。
  • 那如果NSArray count 想要避免符号溢出的麻烦,为什么要返回NSUInteger?这可能是算术启动中更常用的值。
【解决方案3】:

我认为 NSIndexPath 上的 UIKit Additions 故意使用 NSInteger 类型。如果由于某种原因,负行将作为parameter 传递给任何方法(虽然我目前没有看到...),则不会发生自动转换为NSIntegerMax + parameter 值并且任何可能的对象都不会寻找一个可笑的大不存在的参数。不过,还有其他方法可以防止这种情况发生,所以这可能只是个人喜好问题。

例如,我不会将NSUInteger 作为NSIndexPath 类中的参数,而是使用NSInteger 并检查符号,如果任何参数为负,则根本不会创建NSIndexPath

【讨论】:

    猜你喜欢
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多