为什么 NSIndexPath 的 row 属性是有符号整数?
它会不会出现“有效”的负值?
编辑
直到今天我设置 LLVM 来检查符号比较时,我才想到这一点。这使得编译器在有indexPath.row <= [someArray count]
或类似的时候发出警告。
为什么 NSIndexPath 的 row 属性是有符号整数?
它会不会出现“有效”的负值?
编辑
直到今天我设置 LLVM 来检查符号比较时,我才想到这一点。这使得编译器在有indexPath.row <= [someArray count]
或类似的时候发出警告。
使用负值是不明智的,如果你这样做,你会得到疯狂的结果
NSIndexPath* path = [NSIndexPath indexPathForRow:-2 inSection:0];
上面的结果是 0 的部分和 4294967294 的行(这NSUInteger
对我来说看起来像是下溢!)请注意,这只会发生在 UIKit Additions 类别中,而不是 NSIndexPath 本身。看看背后的概念NSIndexPath
,持有负值真的没有意义。所以为什么?
OS X的核心对象NSIndexPath
使用NSUInteger
s 作为其索引,但 UIKit Addition 使用NSInteger
. 该类别仅建立在核心对象之上,但使用NSInteger
overNSUInteger
并没有提供任何额外的功能。
为什么它会这样工作,我不知道。我的猜测(我规定猜测)是第一次启动 iOS 时的一个幼稚的 API 失误。在UITableView
iOS 2 中发布时,它使用NSInteger
s 表示各种事物(例如numberOfSections
)。想一想:这在概念上没有意义,你不能有负数的部分。现在即使在 iOS 6 中,它仍然使用NSInteger
, 所以不会破坏以前的应用程序与表格视图的兼容性。
除此之外UITableView
,我们还添加了NSIndexPath
,它们与表视图一起用于访问它的行等。因为它们必须一起工作,所以它们需要兼容的类型(在这种情况下NSInteger
)。
将类型更改为NSUInteger
全面会破坏很多东西,并且为了安全的 API 设计,所有内容都需要重命名,以便 NSInteger 和 NSUInteger 对应物可以安全地并肩工作。Apple 可能不想要这种麻烦(开发人员也不想要!),因此他们将其保留为NSInteger
.
一个可能的原因是无符号类型很容易下溢。例如,我的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
因此,即使在使用负值没有意义(笔画宽度不能为负)的情况下,在负上下文中使用该值也是有意义的,这会导致下溢。切换到有符号类型会减少意外,同时仍保持范围相当高。听起来是个不错的妥协。
我认为 UIKit Additions 是故意NSIndexPath
使用类型的。NSInteger
如果由于某种原因将负行传递parameter
给任何方法(虽然我目前没有看到...),则NSIntegerMax + parameter
不会发生自动转换为值,并且任何可能的对象都不会寻找不存在的大得离谱的参数。不过,还有其他方法可以防止这种情况发生,所以这可能只是口味问题。
例如,我不会NSUInteger
在类中将其作为参数NSIndexPath
,而是NSInteger
会检查一个符号NSIndexPath
,如果任何参数为负数,则根本不会创建。