如果我在Swift中有一个数组,并尝试访问一个越界的索引,有一个不足为奇的运行时错误:

var str = ["Apple", "Banana", "Coconut"]

str[0] // "Apple"
str[3] // EXC_BAD_INSTRUCTION

然而,我本以为有了Swift带来的所有可选的链接和安全性,做这样的事情是微不足道的:

let theIndex = 3
if let nonexistent = str[theIndex] { // Bounds check + Lookup
    print(nonexistent)
    ...do other things with nonexistent...
}

而不是:

let theIndex = 3
if (theIndex < str.count) {         // Bounds check
    let nonexistent = str[theIndex] // Lookup
    print(nonexistent)   
    ...do other things with nonexistent... 
}

但事实并非如此——我必须使用ol' if语句来检查并确保索引小于str.count。

我尝试添加我自己的下标()实现,但我不确定如何将调用传递给原始实现,或者访问项目(基于索引)而不使用下标符号:

extension Array {
    subscript(var index: Int) -> AnyObject? {
        if index >= self.count {
            NSLog("Womp!")
            return nil
        }
        return ... // What?
    }
}

当前回答

2022

无限索引访问和安全idx访问(如果没有这样的idex,则返回nil):

public extension Collection {
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }

    subscript (infinityIdx idx: Index) -> Element where Index == Int {
        return self[ abs(idx) % self.count ]
    }
}

但是要小心,如果数组/集合为空,它将抛出异常

使用

(0...10)[safe: 11] // nil

(0...10)[infinityIdx: 11] // 0
(0...10)[infinityIdx: 12] // 1
(0...10)[infinityIdx: 21] // 0
(0...10)[infinityIdx: 22] // 1

其他回答

我对数组做了一个简单的扩展

extension Array where Iterator.Element : AnyObject {
    func iof (_ i : Int ) -> Iterator.Element? {
        if self.count > i {
            return self[i] as Iterator.Element
        }
        else {
            return nil
        }
    }

}

它的设计完美无缺

例子

   if let firstElemntToLoad = roots.iof(0)?.children?.iof(0)?.cNode, 

适用于Swift 2

尽管这个问题已经被回答过很多次了,但我想给出一个更符合Swift编程时尚走向的答案,用Crusty的话来说就是:“先考虑协议”。

• What do we want to do? - Get an Element of an Array given an Index only when it's safe, and nil otherwise • What should this functionality base it's implementation on? - Array subscripting • Where does it get this feature from? - Its definition of struct Array in the Swift module has it • Nothing more generic/abstract? - It adopts protocol CollectionType which ensures it as well • Nothing more generic/abstract? - It adopts protocol Indexable as well... • Yup, sounds like the best we can do. Can we then extend it to have this feature we want? - But we have very limited types (no Int) and properties (no count) to work with now! • It will be enough. Swift's stdlib is done pretty well ;)

extension Indexable {
    public subscript(safe safeIndex: Index) -> _Element? {
        return safeIndex.distanceTo(endIndex) > 0 ? self[safeIndex] : nil
    }
}

不正确,但它给出了一个概念

你可以试试

if index >= 0 && index < array.count {
    print(array[index]) 
}

当你只需要从一个数组中获取值,并且你不介意一个小的性能损失(即如果你的集合不是很大),有一个基于dictionary的替代方案,它不涉及(对我来说太通用了)集合扩展:

// Assuming you have a collection named array:
let safeArray = Dictionary(uniqueKeysWithValues: zip(0..., array))
let value = safeArray[index] ?? defaultValue;
extension Array {
  subscript (safe index: UInt) -> Element? {
    return Int(index) < count ? self[Int(index)] : nil
  }
}

使用上述提到的扩展返回nil,如果任何时候索引超出界限。

let fruits = ["apple","banana"]
print("result-\(fruits[safe : 2])")

结果-无