【问题标题】:How do I catch "Index out of range" in Swift?如何在 Swift 中捕获“索引超出范围”?
【发布时间】:2016-09-10 09:45:44
【问题描述】:

我真的很想在我的 Swift 代码中使用更简单的经典 try catch 块,但我找不到任何可以做到这一点的东西。

我只需要:

try {
// some code that causes a crash.
}
catch {
// okay well that crashed, so lets ignore this block and move on.
}  

这是我的困境,当 TableView 重新加载新数据时,一些信息仍位于 RAM 中,这会在 tableView 上调用 didEndDisplayingCell 并使用新的空数据源崩溃。

所以我经常抛出异常Index out of range

我试过了:

func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

    do {
        let imageMessageBody = msgSections[indexPath.section].msg[indexPath.row] as? ImageMessageBody
        let cell = tableView.dequeueReusableCellWithIdentifier("ImageUploadCell", forIndexPath: indexPath) as! ImageCell
        cell.willEndDisplayingCell()
    } catch {
        print("Swift try catch is confusing...")
    }
}

我也试过这个:

func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    print(indexPath.section)
    print(indexPath.row)

    if msgSections.count != 0 {
        if let msg = msgSections[indexPath.section].msg[indexPath.row] as? ImageMessageBody {
            let cell = tableView.dequeueReusableCellWithIdentifier("ImageUploadCell", forIndexPath: indexPath) as! ImageCell
            cell.willEndDisplayingCell()
        }
    }
}

这是一个非常低优先级的代码块,我已经浪费了很多时间来试错,以确定 swift 内置的哪个错误处理程序适用于似乎非常独特的情况,当我有大量的场景时,比如这是代码可能崩溃的地方,它不会对用户体验产生任何影响。

简而言之,我不需要任何花哨的东西,但 Swift 似乎有非常具体的错误处理程序,这些错误处理程序根据我是从函数返回值获取值还是从可能不存在的数组索引获取值而有所不同.

是否像其他所有流行的编程语言一样在 Swift 上进行简单的 try catch?

【问题讨论】:

  • 您似乎正在尝试解决错误的问题。您应该解决在重新加载表视图后为什么在旧数据上调用 didEndDisplayCell 的问题。这没有任何意义。为什么会这样?您是否在后台线程上更新表视图的数据?这是一个可能的问题。
  • 一般从不自己调用包含didwillshould的委托方法。它们由框架调用。
  • 你必须在通过索引访问之前检查数组的计数来处理这个问题。
  • 你有没有弄清楚为什么会发生这种情况?

标签: ios swift uitableview


【解决方案1】:

正如 cmets 和其他答案中所建议的,最好避免这种情况。但是,在某些情况下,您可能想要检查一个项目是否存在于数组中,以及它是否安全地返回它。为此,您可以使用下面的 Array 扩展来安全地返回数组项。

斯威夫特 5

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

Generator.Element 变成 Iterator.Element

斯威夫特 3

extension Collection where Indices.Iterator.Element == Index {
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

斯威夫特 2

extension Array {
    subscript (safe index: Int) -> Element? {
        return indices ~= index ? self[index] : nil
    }
}
  • 这样你就永远打不到 Index out of range
  • 您必须检查该项目是否为nil

更多信息请参考this question


在 Xcode 8.3.2 的 Playground 中尝试 Swift3 代码仍然会导致 当我让 ar = [1,3,4] 时“崩溃”,然后让 v = ar[5]。为什么? – 托马斯 坦佩尔曼 5 月 17 日 17:40

您必须使用我们自定义的下标,所以不是let v = ar[5],而是let v = ar[safe: 5]

默认从数组中获取值。

let boo = foo[index]

添加使用自定义下标。

let boo = fee[safe: index]

// And we can warp the result using guard to keep the code going without throwing the exception.
guard let boo = foo[safe: index] else {
  return
}

【讨论】:

  • 我希望这是默认行为
  • 不适合我。你能解释一下这是如何工作的吗?其中的~indices~是什么
  • 这会在不需要任何选项时将选项引入上下文。最好的解决方案是让您的应用程序崩溃。索引越界是程序员错误的标志。您不应该忽略该标志或尝试使用可选标志来吞下它。避免它。
  • @Sulthan,超出范围的索引可能不是由于编程错误。例如,您正在处理作为程序员无法控制的动态数据。语言应该保护程序永远不会以胎儿运行时错误结束。与其他语言相比,这是一个糟糕的语言设计和 Swift 起步阶段的标志。希望这在未来的版本中得到改变。
  • 在 Xcode 8.3.2 的 Playground 中尝试 Swift3 代码仍然会导致“崩溃”,当我执行 let ar = [1,3,4]let v = ar[5] 时。为什么?
【解决方案2】:

Swift 的Error Handling (do/try/catch) 不是解决运行时异常如“索引超出范围”的解决方案。

运行时异常(您可能还会看到这些称为trap致命错误断言失败等)是程序员的标志错误。除了在-Ounchecked 构建中,Swift 通常保证这些会crash 你的程序,而不是继续在错误/未定义的状态下执行。这些类型的崩溃可能源于使用! 强制展开、隐式展开、滥用unowned 引用、溢出的整数运算/转换、fatalError()s 和precondition()s 和assert()s 等。(而且,不幸的是,Objective-C 异常。)

解决方案是简单地避免这些情况。在您的情况下,检查数组的边界:

if indexPath.section < msgSections.count && indexPath.row < msgSections[indexPath.section].msg.count {
    let msg = msgSections[indexPath.section].msg[indexPath.row]
    // ...
}

(或者,正如 rmaddy 在 cmets 中所说的那样——调查为什么会发生这个问题!它根本不应该发生。)

【讨论】:

  • 那么在什么情况下会导致index out of error呢? for i in 0...magnitudes.count-1 { barHeight = magnitudes[i]*maxHeight; } - 我在我的应用程序中随机得到这个,并且幅度数组在任何其他线程中都没有改变(我可以看到)。我还检查了这个循环之前的计数是否大于 0。
  • 我建议你问一个新问题:stackoverflow.com/questions/ask
  • 很公平。在进行了一些记录之后,我发现计数值从预期值(只有 16)跳到了数十亿的某个模糊数字。我认为这是一个内存问题,所以我可能会继续这样做。只是认为可能有一些明显的原因导致计数在没有开发人员启动的情况下发生变化。
【解决方案3】:

斯威夫特 4:

extension Collection where Indices.Iterator.Element == Index {
    subscript (exist index: Index) -> Iterator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

用法

var index :Int = 6 // or whatever number you need
if let _ = myArray[exist: index] {
   // do stuff
}

var index :Int = 6 // or whatever number you need
guard let _ = myArray[exist: index] else { return }

【讨论】:

    【解决方案4】:

    您可以尝试不同的方法。一定会奏效!

    if msgSections != nil {
        for msg in msgSections[indexPath.section] {
            if msgSections[indexPath.section].index(of: msg) == indexPath.row {
                (Code)
            }
    }
    

    【讨论】:

      【解决方案5】:

      ThisSwift 5 中的回答变成:

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

      否则,从 Swift 4 可以在关联类型上有子句,它可以变成:

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

      【讨论】:

        猜你喜欢
        • 2018-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-11
        • 2016-02-07
        • 1970-01-01
        • 2016-11-10
        相关资源
        最近更新 更多