我应该将NSNotFound与NSInteger或NSUInteger进行比较吗?

时间:2015-07-10 02:38:39

标签: ios objective-c

这是我的代码:

NSArray *allIds = [self.purchasableObjects valueForKey:@"productIdentifier"];
NSInteger index = [allIds indexOfObject:productId];

if (index == NSNotFound)
    return;

NSNotFound ... NSIntegerNSUInteger进行比较哪一个以及为什么?

2 个答案:

答案 0 :(得分:2)

他被问到这个问题的原因是因为AppKit与Foundation索引不一致,特别是NSMenuNSTableView

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.numberOfItemsNSInteger)时,通常(但不保证),该方法等同于{ {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元素。还有一些特定于范围的块方法可以进一步限制值。我建议您阅读NSArrayNSMutableArray文档以获取指针。通过向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。

希望它有所帮助。 感谢