为了账号安全,请及时绑定邮箱和手机立即绑定

通过可选绑定在Swift中进行安全(边界检查)数组查找?

通过可选绑定在Swift中进行安全(边界检查)数组查找?

慕婉清6462132 2019-08-09 15:45:41
通过可选绑定在Swift中进行安全(边界检查)数组查找?如果我在Swift中有一个数组,并尝试访问超出范围的索引,则会出现一个不足为奇的运行时错误:var str = ["Apple", "Banana", "Coconut"]str[0] // "Apple"str[3] // EXC_BAD_INSTRUCTION但是,我会想到Swift带来的所有可选链接和安全性,这样做会很简单:let theIndex = 3if let nonexistent = str[theIndex] { // Bounds check + Lookup    print(nonexistent)     ...do other things with nonexistent...}代替:let theIndex = 3if (theIndex < str.count) {         // Bounds check    let nonexistent = str[theIndex] // Lookup    print(nonexistent)        ...do other things with nonexistent... }但事实并非如此 - 我必须使用ol' if语句来检查并确保索引小于str.count。我尝试添加自己的subscript()实现,但我不知道如何将调用传递给原始实现,或者不使用下标符号来访问项目(基于索引):extension Array {     subscript(var index: Int) -> AnyObject? {         if index >= self.count {             NSLog("Womp!")             return nil         }         return ... // What?    }}
查看完整描述

3 回答

?
莫回无

TA贡献1865条经验 获得超7个赞

我偶然发现了一种更好的实现此功能的方法:

Swift 3.2和更新版本

extension Collection {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }}

Swift 3.0和3.1

extension Collection where Indices.Iterator.Element == Index {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }}

斯威夫特2

extension CollectionType {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }}

let array = [1, 2, 3]for index in -20...20 {
    if let item = array[safe: index] {
        print(item)
    }}


查看完整回答
反对 回复 2019-08-09
?
慕少森

TA贡献2019条经验 获得超9个赞

如果你真的想要这种行为,它就像你想要一个Dictionary而不是一个数组。字典nil在访问丢失的密钥时返回,这是有道理的,因为要知道密钥是否存在于字典中要困难得多,因为这些密钥可以是任何东西,在数组中密钥必须在以下范围内:0to count。迭代这个范围是非常常见的,你可以绝对肯定在循环的每次迭代中都有一个真正的值。

我认为它不能以这种方式工作的原因是Swift开发人员做出的设计选择。举个例子:

var fruits: [String] = ["Apple", "Banana", "Coconut"]var str: String = "I ate a \( fruits[0] )"

如果您已经知道索引存在,就像在大多数使用数组的情况下一样,这段代码很棒。但是,如果访问标可能可能返回nil,那么你已经改变了返回类型Arraysubscript方法是可选的。这会将您的代码更改为:

var fruits: [String] = ["Apple", "Banana", "Coconut"]var str: String = "I ate a \( fruits[0]! )"//                                     ^ Added

这意味着每次迭代数组时都需要解包一个可选项,或者使用已知索引执行任何其他操作,因为很少有人可以访问超出范围的索引。Swift设计者在访问越界索引时以牺牲运行时异常为代价,选择了较少的可选解包。崩溃比nil你在某个地方没想到的逻辑错误更可取。

我同意他们的观点。因此,您将不会更改默认Array实现,因为您将破坏所有需要来自数组的非可选值的代码。

相反,您可以子类化Array,并覆盖subscript以返回可选项。或者,更实际地,您可以Array使用执行此操作的非下标方法进行扩展。

extension Array {

    // Safely lookup an index that might be out of bounds,    // returning nil if it does not exist    func get(index: Int) -> T? {
        if 0 <= index && index < count {
            return self[index]
        } else {
            return nil
        }
    }}var fruits: [String] = ["Apple", "Banana", "Coconut"]if let fruit = fruits.get(1) {
    print("I ate a \( fruit )")
    // I ate a Banana}if let fruit = fruits.get(3) {
    print("I ate a \( fruit )")
    // never runs, get returned nil}

Swift 3更新

func get(index: Int) ->T? 需要被替换 func get(index: Int) ->Element?


查看完整回答
反对 回复 2019-08-09
  • 3 回答
  • 0 关注
  • 549 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信