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

19

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

它是否会取到“有效”的负值?

enter image description here

编辑

直到我将LLVM设置为检查符号比较时,我才想到这个问题。这使得编译器在indexPath.row <= [someArray count]或类似语句中发出警告。


为什么需要行的负值?它是只读的,所以您不必担心 :) - Valentin Shamardin
1
我不需要行的负值,我只是想知道为什么行不是一个NSUInteger。 - MdaG
1
"它是只读的,所以您不必关心它"; 您应该知道,initWithRow:section:采用NSInteger. - WDUK
2
好问题,我猜(开玩笑)是因为苹果不希望您使用超过 2,147,483,647 个单元格的表格! - Mick MacCallum
@0x7fffffff♦ - 你让我开心了 :) - PiotrDomo
3个回答

13

如果使用负数会发生什么?

使用负值并不明智,如果使用,您将得到疯狂的结果。

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

上述结果中,一个部分的值为0,另一个行的值为4294967294(对我来说看起来像是NSUInteger下溢!)请放心,这仅在UIKit增加类别中发生,而不是在NSIndexPath本身中发生。从NSIndexPath背后的概念来看,保留负值真的没有意义。那么为什么会这样呢?

可能的原因

来自OS X的核心对象NSIndexPath使用NSUInteger作为其索引,但是UIKit Additions使用NSInteger。该类别仅在基础对象之上构建,但是使用NSInteger而不是NSUInteger并没有提供任何额外的功能。

为什么要这样工作,我不知道。我猜测(并且我强调只是猜测),这是在首次发布iOS时一个天真的API失误。当UITableView在iOS 2中发布时,它使用NSInteger进行各种事情(例如numberOfSections)。想想看:这在概念上是不合理的,你不能有负数个部分。即使在iOS 6中,它仍然使用NSInteger,以便不破坏与表视图之前的应用程序兼容性。

除了UITableView之外,我们还有对NSIndexPath的增加,它与表视图一起用于访问其行等。因为它们必须共同工作,所以它们需要兼容类型(在这种情况下是NSInteger)。

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


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

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)不必关心用户级别的算术运算。 - WDUK
当然我可以自己转换数字,但问题是我需要意识到这个问题才能做到。无符号类型在这里真的没有任何优势,对吧? - zoul
1
这在概念上是正确的;你不能有负数个部分,因此类型应该尝试强制执行这一点。 - WDUK
2
那么,如果他们想避免符号溢出的麻烦,为什么NSArray count会返回NSUInteger呢?这可能是算术中更常用的值。 - WDUK
是的,SDK并不完全一致。无论我们得出什么原因,可能只是某些在设计API时有意义但现在不值得改变的小细节。 - zoul
显示剩余2条评论

0

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

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


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