这是我的代码:
NSArray *allIds = [self.purchasableObjects valueForKey:@"productIdentifier"];
NSInteger index = [allIds indexOfObject:productId];
if (index == NSNotFound)
return;
与NSNotFound
... NSInteger
或NSUInteger
进行比较哪一个以及为什么?
答案 0 :(得分:2)
他被问到这个问题的原因是因为AppKit与Foundation索引不一致,特别是NSMenu
和NSTableView
:
id obj = ...;
NSMenu * menu = ...;
/* This returns an NSInteger, returning -1 (which when casted, is NSNotFound) */
NSInteger index = [ menu indexOfItemWithRepresentedObject:obj ];
/* This returns an NSUInteger, since arrays cannot have negative indices. */
NSUInteger idx = [ [ menu itemArray ] indexOfObjectIdenticalTo:obj ];
因此,必须来回投射,但这会产生新的问题:
因为索引不能为负数,所以行数(或子菜单, 等)正常数组可以低一半。
如果您尝试在UI元素中读取或插入对象到负索引(-2),则不会获得范围异常。如果转换为无符号,则索引未定义。所以这个错误的代码编译没有错误:
id obj = ...;
NSMenu * menu = ...;
/* Or the result of some integer arithmetic. */
NSInteger idx = -2;
/* Compiles, doesn't throw exception. Index COULD be NSIntegerMax - 1, but the ending index is undefined. */
[ menu insertItem:obj atIndex:idx ];
/* Compiles, but throws NSRangeException. */
[ [ menu mutableArrayValueForKey:@"items" ] insertObject:obj atIndex:( NSUInteger )idx ];
在第一种情况下,当插入项目超出 menu.numberOfItems
(NSInteger
)时,通常(但不保证),该方法等同于{ {1}}。因此,索引因此被转换:
[ menu addItem ]
但是在旧版本的AppKit中,索引被四舍五入!:
[ menu insertItem:obj atIndex:MIN( idx, menu.numberOfItems ) ];
因此,程序员在从UI元素插入和/或检索索引时必须非常小心,或者使用[ menu insertItem:obj atIndex:MAX( MIN( idx, menu.numberOfItems ), 0 ) ];
代替。
对于所有情况,虽然 可以安全地检查NSArrayController
和[ menu indexOfItem:obj ]
相等 [ array indexOfObjectIdenticalTo:obj ]
,但只要你使用一个显式的演员。虽然NSNotFound
的声明类型为NSNotFound
,但已定义为NSUInteger
,当施法时等于-1时。 从不检查某个值是否小于NSUIntegerMax
,因为您将创建另一个无限循环。但随意:
NSNotFound
或
NSInteger index = [ menu indexOfItem:nonexistantObject ];
if( ( NSUInteger )index == NSNotFound ) {
...blah...
}
但是,如果返回类型为NSUInteger index = [ array indexOfItem:nonexistantObject ];
if( index == NSNotFound ) {
...blah...
}
,则不假设返回的索引不是NSInteger
它是有效索引(特别是如果你'使用第三方框架或库)。相反,您应该检查返回的索引是否在有效范围内:
NSNotFound
当然,该检查的计算成本很高,因此它只应用于不经常运行的代码,循环中不或调试代码中。如果要确保始终尽快获得有效索引,则应使用块枚举,这比任何其他方法更快:
NSInteger index = [ menu indexOfItem:nonexistantObject ];
if( ( NSUInteger )index == NSNotFound ) {
/* Can't find it, so insert or do something else. */
} else if( !( index >= 0 && index <= menu.numberOfItems ) ) {
/* Throw an invalid range exception. There's a major bug! */
} else {
/* woo hoo! we found it! */
}
返回的索引保证有效,id obj = ..;
/* Get an array from the UI, or just use a normal array. */
NSArray * array = [ menu itemArray ];
/* Find an index quickly */
NSUInteger index = [ array indexOfObjectWithOptions:NSEnumerationConcurrent passingTest:^BOOL( NSUInteger idx, id testObj, BOOL * stop ) {
if( obj == testObj ) {
*stop = TRUE;
return TRUE;
}
} ];
或NSNotFound
。注意我没有使用NSMakeRange( 0, array.count - 1 )
,因为我检查的是特定的实例而不是可以被解释为相同的对象。
如果你需要对循环中的索引做一些事情,你可以使用相同的技术(注意索引被传递给块,总是有效):
-isEqual:
使用数组上的块方法总是获取有效的索引,可以转换为[ array enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^( NSUInteger idx, id obj, BOOL * stop ) {
/* do something to this object, like adding it to another array, etc */
} ];
以用于UI元素。还有一些特定于范围的块方法可以进一步限制值。我建议您阅读NSArray和NSMutableArray文档以获取指针。通过向NSIntegers
方法添加代码也很容易误用块,特别是如果要将对象添加到另一个集合类中:
[enumerate...withBlock:][3]
最好这样做:
NSArray * source = ...;
NSMutableArray * dest = ...;
[ source enumerateObjectsUsingBlock:^( NSUInteger idx, id obj, BOOL * stop ) {
if( /* some test */ ) {
/* This is wasteful, and possibly incorrect if you're using NSEnumerationConcurrent */
[ dest addObject:obj ];
}
} ];
对于冗长的响应感到抱歉,但这是我的开发人员必须解决并重新解决的Cocoa索引(尤其是/* block removed for clarity */
NSArray * source = ...;
NSMutableArray * dest = ...;
NSIndexSet * indices = [ source indexesOfObjectsPassingTest:testBlock ];
[ dest addObjects:[ source objectsAtIndexes:indices ] ];
)的问题,因此我认为分享会有所帮助。 / p>
答案 1 :(得分:1)
您应该使用NSUInteger
背后的原因是&#34;数组索引不会减去(即索引为-5的对象)&#34;
typedef int NSInteger;
typedef unsigned int NSUInteger;
当有可能获得正或负的值时,必须使用NSInteger。
希望它有所帮助。 感谢