的原因就是他爲什麼問這個問題是因爲AppKit的是基金指數不一致,特別是對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
),一般(但不能保證),該方法是相當於[ menu addItem ]
。所以,指數因而轉化:
[ menu insertItem:obj atIndex:MIN(idx, menu.numberOfItems) ];
但在老版本了AppKit中,指數向上取整!:
[ menu insertItem:obj atIndex:MAX(MIN(idx, menu.numberOfItems), 0) ];
所以程序員必須在插入時和/或檢索從UI指標採取非常謹慎元素,或使用NSArrayController
代替。
對於所有的情況,雖然它是安全檢查都[ menu indexOfItem:obj ]
和[ array indexOfObjectIdenticalTo:obj ]
是等於NSNotFound
,只要您使用顯式類型轉換。雖然NSNotFound
具有NSUInteger
的聲明類型,但是定義爲爲NSUIntegerMax
,當投射等於-1時。 從不檢查一個值是否小於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 */
} ];
使用塊上的方法總是得到你有效的索引,可以將其轉換爲NSIntegers
以用於UI元素。還有範圍特定的塊方法可以進一步限制值。我建議通過NSArray和NSMutableArray文檔獲取指針。它也很容易誤塊,通過將代碼添加到[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 ];
}
} ];
這是更好地做到這一點,而不是:
/* block removed for clarity */
NSArray * source = ...;
NSMutableArray * dest = ...;
NSIndexSet * indices = [ source indexesOfObjectsPassingTest:testBlock ];
[ dest addObjects:[ source objectsAtIndexes:indices ] ];
對不起對於囉嗦的迴應,但這是Cocoa索引(特別是NSNotFound
)的一個問題,我的開發人員必須解決並重新解決這個問題,所以我認爲這將有助於分享。
看看'NSArray indexOfObject:'的文檔。它的返回類型是什麼? – rmaddy
這兩個工作。看到http://stackoverflow.com/questions/9338295/why-nsnotfound-isnt-1-but-nsintegermax –